2020-03-06 21:45:19

by Michael Walle

[permalink] [raw]
Subject: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

The DMA channel might not be available at probe time. This is esp. the
case if the DMA controller has an IOMMU mapping.

There is also another caveat. If there is no DMA controller at all,
dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot test
for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will fail to
probe if, for example, the DMA driver is not enabled in the kernel
configuration.

To workaround this, we request the DMA channel in _startup(). Other
serial drivers do it the same way.

Signed-off-by: Michael Walle <[email protected]>
---
drivers/tty/serial/fsl_lpuart.c | 88 +++++++++++++++++++++------------
1 file changed, 57 insertions(+), 31 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index c31b8f3db6bf..33798df4d727 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct lpuart_port *sport)
static void lpuart_tx_dma_startup(struct lpuart_port *sport)
{
u32 uartbaud;
+ int ret;

- if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
- init_waitqueue_head(&sport->dma_wait);
- sport->lpuart_dma_tx_use = true;
- if (lpuart_is_32(sport)) {
- uartbaud = lpuart32_read(&sport->port, UARTBAUD);
- lpuart32_write(&sport->port,
- uartbaud | UARTBAUD_TDMAE, UARTBAUD);
- } else {
- writeb(readb(sport->port.membase + UARTCR5) |
- UARTCR5_TDMAS, sport->port.membase + UARTCR5);
- }
+ 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;
+ }
+
+ ret = lpuart_dma_tx_request(&sport->port);
+ if (!ret)
+ goto err;
+
+ init_waitqueue_head(&sport->dma_wait);
+ sport->lpuart_dma_tx_use = true;
+ if (lpuart_is_32(sport)) {
+ uartbaud = lpuart32_read(&sport->port, UARTBAUD);
+ lpuart32_write(&sport->port,
+ uartbaud | UARTBAUD_TDMAE, UARTBAUD);
} else {
- sport->lpuart_dma_tx_use = false;
+ writeb(readb(sport->port.membase + UARTCR5) |
+ UARTCR5_TDMAS, sport->port.membase + UARTCR5);
}
+
+ return;
+
+err:
+ sport->lpuart_dma_tx_use = false;
}

static void lpuart_rx_dma_startup(struct lpuart_port *sport)
{
- if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
- /* set Rx DMA timeout */
- sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
- if (!sport->dma_rx_timeout)
- sport->dma_rx_timeout = 1;
+ int ret;

- sport->lpuart_dma_rx_use = true;
- rx_dma_timer_init(sport);
- } else {
- sport->lpuart_dma_rx_use = false;
+ 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;
+ goto err;
}
+
+ ret = lpuart_start_rx_dma(sport);
+ if (ret)
+ goto err;
+
+ /* set Rx DMA timeout */
+ sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+ if (!sport->dma_rx_timeout)
+ sport->dma_rx_timeout = 1;
+
+ sport->lpuart_dma_rx_use = true;
+ rx_dma_timer_init(sport);
+
+ return;
+
+err:
+ sport->lpuart_dma_rx_use = false;
}

static int lpuart_startup(struct uart_port *port)
@@ -1615,6 +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
dmaengine_terminate_all(sport->dma_tx_chan);
}
}
+
+ if (sport->dma_tx_chan)
+ dma_release_channel(sport->dma_tx_chan);
+ if (sport->dma_rx_chan)
+ dma_release_channel(sport->dma_rx_chan);
}

static void lpuart_shutdown(struct uart_port *port)
@@ -2520,16 +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)

sport->port.rs485_config(&sport->port, &sport->port.rs485);

- sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
- if (!sport->dma_tx_chan)
- dev_info(sport->port.dev, "DMA tx channel request failed, "
- "operating without tx DMA\n");
-
- sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
- if (!sport->dma_rx_chan)
- dev_info(sport->port.dev, "DMA rx channel request failed, "
- "operating without rx DMA\n");
-
return 0;

failed_attach_port:
--
2.20.1


2020-03-24 15:28:57

by Leonard Crestez

[permalink] [raw]
Subject: Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

On 06.03.2020 23:44, Michael Walle wrote:
> The DMA channel might not be available at probe time. This is esp. the
> case if the DMA controller has an IOMMU mapping.
>
> There is also another caveat. If there is no DMA controller at all,
> dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot test
> for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will fail to
> probe if, for example, the DMA driver is not enabled in the kernel
> configuration.
>
> To workaround this, we request the DMA channel in _startup(). Other
> serial drivers do it the same way.
>
> Signed-off-by: Michael Walle <[email protected]>

This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
next-20200324 if this patch is reverted)

> ---
> drivers/tty/serial/fsl_lpuart.c | 88 +++++++++++++++++++++------------
> 1 file changed, 57 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
> index c31b8f3db6bf..33798df4d727 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct lpuart_port *sport)
> static void lpuart_tx_dma_startup(struct lpuart_port *sport)
> {
> u32 uartbaud;
> + int ret;
>
> - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
> - init_waitqueue_head(&sport->dma_wait);
> - sport->lpuart_dma_tx_use = true;
> - if (lpuart_is_32(sport)) {
> - uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> - lpuart32_write(&sport->port,
> - uartbaud | UARTBAUD_TDMAE, UARTBAUD);
> - } else {
> - writeb(readb(sport->port.membase + UARTCR5) |
> - UARTCR5_TDMAS, sport->port.membase + UARTCR5);
> - }
> + 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));

It seems that this since this is called from lpuart32_startup with
&sport->port.lock held and lpuart32_console_write takes the same lock it
can and hang.

As a workaround I can just remove this print but there are other
possible error conditions in dmaengine code which can cause a printk.

Maybe the port lock should only be held around register manipulation?

> + sport->dma_tx_chan = NULL;
> + goto err;
> + }
> +
> + ret = lpuart_dma_tx_request(&sport->port);
> + if (!ret)
> + goto err;

This is backwards: lpuart_dma_tx_request returns negative errno on failure.

> +
> + init_waitqueue_head(&sport->dma_wait);
> + sport->lpuart_dma_tx_use = true;
> + if (lpuart_is_32(sport)) {
> + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> + lpuart32_write(&sport->port,
> + uartbaud | UARTBAUD_TDMAE, UARTBAUD);
> } else {
> - sport->lpuart_dma_tx_use = false;
> + writeb(readb(sport->port.membase + UARTCR5) |
> + UARTCR5_TDMAS, sport->port.membase + UARTCR5);
> }
> +
> + return;
> +
> +err:
> + sport->lpuart_dma_tx_use = false;
> }
>
> static void lpuart_rx_dma_startup(struct lpuart_port *sport)
> {
> - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
> - /* set Rx DMA timeout */
> - sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
> - if (!sport->dma_rx_timeout)
> - sport->dma_rx_timeout = 1;
> + int ret;
>
> - sport->lpuart_dma_rx_use = true;
> - rx_dma_timer_init(sport);
> - } else {
> - sport->lpuart_dma_rx_use = false;
> + 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;
> + goto err;
> }
> +
> + ret = lpuart_start_rx_dma(sport);
> + if (ret)
> + goto err;

This is not backwards.

> +
> + /* set Rx DMA timeout */
> + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
> + if (!sport->dma_rx_timeout)
> + sport->dma_rx_timeout = 1;
> +
> + sport->lpuart_dma_rx_use = true;
> + rx_dma_timer_init(sport);
> +
> + return;
> +
> +err:
> + sport->lpuart_dma_rx_use = false;
> }
>
> static int lpuart_startup(struct uart_port *port)
> @@ -1615,6 +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
> dmaengine_terminate_all(sport->dma_tx_chan);
> }
> }
> +
> + if (sport->dma_tx_chan)
> + dma_release_channel(sport->dma_tx_chan);
> + if (sport->dma_rx_chan)
> + dma_release_channel(sport->dma_rx_chan);
> }
>
> static void lpuart_shutdown(struct uart_port *port)
> @@ -2520,16 +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
>
> sport->port.rs485_config(&sport->port, &sport->port.rs485);
>
> - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
> - if (!sport->dma_tx_chan)
> - dev_info(sport->port.dev, "DMA tx channel request failed, "
> - "operating without tx DMA\n");
> -
> - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
> - if (!sport->dma_rx_chan)
> - dev_info(sport->port.dev, "DMA rx channel request failed, "
> - "operating without rx DMA\n");
> -
> return 0;
>
> failed_attach_port:
>

2020-03-24 16:13:10

by Andy Duan

[permalink] [raw]
Subject: RE: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

From: Leonard Crestez <[email protected]> Sent: Tuesday, March 24, 2020 11:27 PM
> On 06.03.2020 23:44, Michael Walle wrote:
> > The DMA channel might not be available at probe time. This is esp. the
> > case if the DMA controller has an IOMMU mapping.
> >
> > There is also another caveat. If there is no DMA controller at all,
> > dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot test
> > for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will fail to
> > probe if, for example, the DMA driver is not enabled in the kernel
> > configuration.
If DMA driver is not enabled, we should disable DMA controller node in
dts file to match current sw environment, then driver doesn't do defer probe.

So I still suggest to check -EPROBE_DEFER for dma_request_slave_channel() in
.probe() function.

Andy
> >
> > To workaround this, we request the DMA channel in _startup(). Other
> > serial drivers do it the same way.
> >
> > Signed-off-by: Michael Walle <[email protected]>
>
> This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
> next-20200324 if this patch is reverted)
>
> > ---
> > drivers/tty/serial/fsl_lpuart.c | 88 +++++++++++++++++++++------------
> > 1 file changed, 57 insertions(+), 31 deletions(-)
> >
> > diff --git a/drivers/tty/serial/fsl_lpuart.c
> > b/drivers/tty/serial/fsl_lpuart.c index c31b8f3db6bf..33798df4d727
> > 100644
> > --- a/drivers/tty/serial/fsl_lpuart.c
> > +++ b/drivers/tty/serial/fsl_lpuart.c
> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
> lpuart_port *sport)
> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
> > {
> > u32 uartbaud;
> > + int ret;
> >
> > - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
> > - init_waitqueue_head(&sport->dma_wait);
> > - sport->lpuart_dma_tx_use = true;
> > - if (lpuart_is_32(sport)) {
> > - uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> > - lpuart32_write(&sport->port,
> > - uartbaud | UARTBAUD_TDMAE, UARTBAUD);
> > - } else {
> > - writeb(readb(sport->port.membase + UARTCR5) |
> > - UARTCR5_TDMAS, sport->port.membase + UARTCR5);
> > - }
> > + 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));
>
> It seems that this since this is called from lpuart32_startup with
> &sport->port.lock held and lpuart32_console_write takes the same lock it can
> and hang.
>
> As a workaround I can just remove this print but there are other possible error
> conditions in dmaengine code which can cause a printk.
>
> Maybe the port lock should only be held around register manipulation?
>
> > + sport->dma_tx_chan = NULL;
> > + goto err;
> > + }
> > +
> > + ret = lpuart_dma_tx_request(&sport->port);
> > + if (!ret)
> > + goto err;
>
> This is backwards: lpuart_dma_tx_request returns negative errno on failure.
>
> > +
> > + init_waitqueue_head(&sport->dma_wait);
> > + sport->lpuart_dma_tx_use = true;
> > + if (lpuart_is_32(sport)) {
> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> > + lpuart32_write(&sport->port,
> > + uartbaud | UARTBAUD_TDMAE, UARTBAUD);
> > } else {
> > - sport->lpuart_dma_tx_use = false;
> > + writeb(readb(sport->port.membase + UARTCR5) |
> > + UARTCR5_TDMAS, sport->port.membase + UARTCR5);
> > }
> > +
> > + return;
> > +
> > +err:
> > + sport->lpuart_dma_tx_use = false;
> > }
> >
> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
> > {
> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
> > - /* set Rx DMA timeout */
> > - sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
> > - if (!sport->dma_rx_timeout)
> > - sport->dma_rx_timeout = 1;
> > + int ret;
> >
> > - sport->lpuart_dma_rx_use = true;
> > - rx_dma_timer_init(sport);
> > - } else {
> > - sport->lpuart_dma_rx_use = false;
> > + 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;
> > + goto err;
> > }
> > +
> > + ret = lpuart_start_rx_dma(sport);
> > + if (ret)
> > + goto err;
>
> This is not backwards.
>
> > +
> > + /* set Rx DMA timeout */
> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
> > + if (!sport->dma_rx_timeout)
> > + sport->dma_rx_timeout = 1;
> > +
> > + sport->lpuart_dma_rx_use = true;
> > + rx_dma_timer_init(sport);
> > +
> > + return;
> > +
> > +err:
> > + sport->lpuart_dma_rx_use = false;
> > }
> >
> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
> > dmaengine_terminate_all(sport->dma_tx_chan);
> > }
> > }
> > +
> > + if (sport->dma_tx_chan)
> > + dma_release_channel(sport->dma_tx_chan);
> > + if (sport->dma_rx_chan)
> > + dma_release_channel(sport->dma_rx_chan);
> > }
> >
> > static void lpuart_shutdown(struct uart_port *port) @@ -2520,16
> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
> >
> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
> >
> > - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev,
> "tx");
> > - if (!sport->dma_tx_chan)
> > - dev_info(sport->port.dev, "DMA tx channel request failed, "
> > - "operating without tx DMA\n");
> > -
> > - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev,
> "rx");
> > - if (!sport->dma_rx_chan)
> > - dev_info(sport->port.dev, "DMA rx channel request failed, "
> > - "operating without rx DMA\n");
> > -
> > return 0;
> >
> > failed_attach_port:
> >

2020-03-24 16:18:24

by Michael Walle

[permalink] [raw]
Subject: Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

Am 2020-03-24 17:12, schrieb Andy Duan:
> From: Leonard Crestez <[email protected]> Sent: Tuesday, March
> 24, 2020 11:27 PM
>> On 06.03.2020 23:44, Michael Walle wrote:
>> > The DMA channel might not be available at probe time. This is esp. the
>> > case if the DMA controller has an IOMMU mapping.
>> >
>> > There is also another caveat. If there is no DMA controller at all,
>> > dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot test
>> > for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will fail to
>> > probe if, for example, the DMA driver is not enabled in the kernel
>> > configuration.
> If DMA driver is not enabled, we should disable DMA controller node in
> dts file to match current sw environment, then driver doesn't do defer
> probe.
>
> So I still suggest to check -EPROBE_DEFER for
> dma_request_slave_channel() in
> .probe() function.

I don't know if I can follow you here. This would lead to non functional
setups,
eg. one build its own kernel with DMA disabled, but still have a device
tree
with the DMA nodes. And besides, the current workaround to request the
DMA
channel in startup() is basically working, isn't it? And once the
underlying
problem is fixed (the infinite EPROBE_DEFER), it could be moved back
into
_probe().

-michael

>
> Andy
>> >
>> > To workaround this, we request the DMA channel in _startup(). Other
>> > serial drivers do it the same way.
>> >
>> > Signed-off-by: Michael Walle <[email protected]>
>>
>> This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
>> next-20200324 if this patch is reverted)
>>
>> > ---
>> > drivers/tty/serial/fsl_lpuart.c | 88 +++++++++++++++++++++------------
>> > 1 file changed, 57 insertions(+), 31 deletions(-)
>> >
>> > diff --git a/drivers/tty/serial/fsl_lpuart.c
>> > b/drivers/tty/serial/fsl_lpuart.c index c31b8f3db6bf..33798df4d727
>> > 100644
>> > --- a/drivers/tty/serial/fsl_lpuart.c
>> > +++ b/drivers/tty/serial/fsl_lpuart.c
>> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
>> lpuart_port *sport)
>> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
>> > {
>> > u32 uartbaud;
>> > + int ret;
>> >
>> > - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
>> > - init_waitqueue_head(&sport->dma_wait);
>> > - sport->lpuart_dma_tx_use = true;
>> > - if (lpuart_is_32(sport)) {
>> > - uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> > - lpuart32_write(&sport->port,
>> > - uartbaud | UARTBAUD_TDMAE, UARTBAUD);
>> > - } else {
>> > - writeb(readb(sport->port.membase + UARTCR5) |
>> > - UARTCR5_TDMAS, sport->port.membase + UARTCR5);
>> > - }
>> > + 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));
>>
>> It seems that this since this is called from lpuart32_startup with
>> &sport->port.lock held and lpuart32_console_write takes the same lock
>> it can
>> and hang.
>>
>> As a workaround I can just remove this print but there are other
>> possible error
>> conditions in dmaengine code which can cause a printk.
>>
>> Maybe the port lock should only be held around register manipulation?
>>
>> > + sport->dma_tx_chan = NULL;
>> > + goto err;
>> > + }
>> > +
>> > + ret = lpuart_dma_tx_request(&sport->port);
>> > + if (!ret)
>> > + goto err;
>>
>> This is backwards: lpuart_dma_tx_request returns negative errno on
>> failure.
>>
>> > +
>> > + init_waitqueue_head(&sport->dma_wait);
>> > + sport->lpuart_dma_tx_use = true;
>> > + if (lpuart_is_32(sport)) {
>> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> > + lpuart32_write(&sport->port,
>> > + uartbaud | UARTBAUD_TDMAE, UARTBAUD);
>> > } else {
>> > - sport->lpuart_dma_tx_use = false;
>> > + writeb(readb(sport->port.membase + UARTCR5) |
>> > + UARTCR5_TDMAS, sport->port.membase + UARTCR5);
>> > }
>> > +
>> > + return;
>> > +
>> > +err:
>> > + sport->lpuart_dma_tx_use = false;
>> > }
>> >
>> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
>> > {
>> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
>> > - /* set Rx DMA timeout */
>> > - sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
>> > - if (!sport->dma_rx_timeout)
>> > - sport->dma_rx_timeout = 1;
>> > + int ret;
>> >
>> > - sport->lpuart_dma_rx_use = true;
>> > - rx_dma_timer_init(sport);
>> > - } else {
>> > - sport->lpuart_dma_rx_use = false;
>> > + 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;
>> > + goto err;
>> > }
>> > +
>> > + ret = lpuart_start_rx_dma(sport);
>> > + if (ret)
>> > + goto err;
>>
>> This is not backwards.
>>
>> > +
>> > + /* set Rx DMA timeout */
>> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
>> > + if (!sport->dma_rx_timeout)
>> > + sport->dma_rx_timeout = 1;
>> > +
>> > + sport->lpuart_dma_rx_use = true;
>> > + rx_dma_timer_init(sport);
>> > +
>> > + return;
>> > +
>> > +err:
>> > + sport->lpuart_dma_rx_use = false;
>> > }
>> >
>> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
>> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
>> > dmaengine_terminate_all(sport->dma_tx_chan);
>> > }
>> > }
>> > +
>> > + if (sport->dma_tx_chan)
>> > + dma_release_channel(sport->dma_tx_chan);
>> > + if (sport->dma_rx_chan)
>> > + dma_release_channel(sport->dma_rx_chan);
>> > }
>> >
>> > static void lpuart_shutdown(struct uart_port *port) @@ -2520,16
>> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
>> >
>> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
>> >
>> > - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev,
>> "tx");
>> > - if (!sport->dma_tx_chan)
>> > - dev_info(sport->port.dev, "DMA tx channel request failed, "
>> > - "operating without tx DMA\n");
>> > -
>> > - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev,
>> "rx");
>> > - if (!sport->dma_rx_chan)
>> > - dev_info(sport->port.dev, "DMA rx channel request failed, "
>> > - "operating without rx DMA\n");
>> > -
>> > return 0;
>> >
>> > failed_attach_port:
>> >

2020-03-24 16:24:30

by Michael Walle

[permalink] [raw]
Subject: Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

Am 2020-03-24 16:27, schrieb Leonard Crestez:
> On 06.03.2020 23:44, Michael Walle wrote:
>> The DMA channel might not be available at probe time. This is esp. the
>> case if the DMA controller has an IOMMU mapping.
>>
>> There is also another caveat. If there is no DMA controller at all,
>> dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot test
>> for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will fail to
>> probe if, for example, the DMA driver is not enabled in the kernel
>> configuration.
>>
>> To workaround this, we request the DMA channel in _startup(). Other
>> serial drivers do it the same way.
>>
>> Signed-off-by: Michael Walle <[email protected]>
>
> This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
> next-20200324 if this patch is reverted)
>
>> ---
>> drivers/tty/serial/fsl_lpuart.c | 88
>> +++++++++++++++++++++------------
>> 1 file changed, 57 insertions(+), 31 deletions(-)
>>
>> diff --git a/drivers/tty/serial/fsl_lpuart.c
>> b/drivers/tty/serial/fsl_lpuart.c
>> index c31b8f3db6bf..33798df4d727 100644
>> --- a/drivers/tty/serial/fsl_lpuart.c
>> +++ b/drivers/tty/serial/fsl_lpuart.c
>> @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
>> lpuart_port *sport)
>> static void lpuart_tx_dma_startup(struct lpuart_port *sport)
>> {
>> u32 uartbaud;
>> + int ret;
>>
>> - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
>> - init_waitqueue_head(&sport->dma_wait);
>> - sport->lpuart_dma_tx_use = true;
>> - if (lpuart_is_32(sport)) {
>> - uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> - lpuart32_write(&sport->port,
>> - uartbaud | UARTBAUD_TDMAE, UARTBAUD);
>> - } else {
>> - writeb(readb(sport->port.membase + UARTCR5) |
>> - UARTCR5_TDMAS, sport->port.membase + UARTCR5);
>> - }
>> + 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));
>
> It seems that this since this is called from lpuart32_startup with
> &sport->port.lock held and lpuart32_console_write takes the same lock
> it
> can and hang.

Shoot.
So basically, you cannot do any dev_dbg/info/warn/err/pr_* when you
the lock is taken (which could also be taken in serial_core.c), correct?

Because thats all over the place.. just happens now because there is a
dev_info() by default.

>
> As a workaround I can just remove this print but there are other
> possible error conditions in dmaengine code which can cause a printk.
>
> Maybe the port lock should only be held around register manipulation?
>
>> + sport->dma_tx_chan = NULL;
>> + goto err;
>> + }
>> +
>> + ret = lpuart_dma_tx_request(&sport->port);
>> + if (!ret)
>> + goto err;
>
> This is backwards: lpuart_dma_tx_request returns negative errno on
> failure.

nice catch!

-michael

>
>> +
>> + init_waitqueue_head(&sport->dma_wait);
>> + sport->lpuart_dma_tx_use = true;
>> + if (lpuart_is_32(sport)) {
>> + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> + lpuart32_write(&sport->port,
>> + uartbaud | UARTBAUD_TDMAE, UARTBAUD);
>> } else {
>> - sport->lpuart_dma_tx_use = false;
>> + writeb(readb(sport->port.membase + UARTCR5) |
>> + UARTCR5_TDMAS, sport->port.membase + UARTCR5);
>> }
>> +
>> + return;
>> +
>> +err:
>> + sport->lpuart_dma_tx_use = false;
>> }
>>
>> static void lpuart_rx_dma_startup(struct lpuart_port *sport)
>> {
>> - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
>> - /* set Rx DMA timeout */
>> - sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
>> - if (!sport->dma_rx_timeout)
>> - sport->dma_rx_timeout = 1;
>> + int ret;
>>
>> - sport->lpuart_dma_rx_use = true;
>> - rx_dma_timer_init(sport);
>> - } else {
>> - sport->lpuart_dma_rx_use = false;
>> + 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;
>> + goto err;
>> }
>> +
>> + ret = lpuart_start_rx_dma(sport);
>> + if (ret)
>> + goto err;
>
> This is not backwards.
>
>> +
>> + /* set Rx DMA timeout */
>> + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
>> + if (!sport->dma_rx_timeout)
>> + sport->dma_rx_timeout = 1;
>> +
>> + sport->lpuart_dma_rx_use = true;
>> + rx_dma_timer_init(sport);
>> +
>> + return;
>> +
>> +err:
>> + sport->lpuart_dma_rx_use = false;
>> }
>>
>> static int lpuart_startup(struct uart_port *port)
>> @@ -1615,6 +1646,11 @@ static void lpuart_dma_shutdown(struct
>> lpuart_port *sport)
>> dmaengine_terminate_all(sport->dma_tx_chan);
>> }
>> }
>> +
>> + if (sport->dma_tx_chan)
>> + dma_release_channel(sport->dma_tx_chan);
>> + if (sport->dma_rx_chan)
>> + dma_release_channel(sport->dma_rx_chan);
>> }
>>
>> static void lpuart_shutdown(struct uart_port *port)
>> @@ -2520,16 +2556,6 @@ static int lpuart_probe(struct platform_device
>> *pdev)
>>
>> sport->port.rs485_config(&sport->port, &sport->port.rs485);
>>
>> - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev,
>> "tx");
>> - if (!sport->dma_tx_chan)
>> - dev_info(sport->port.dev, "DMA tx channel request failed, "
>> - "operating without tx DMA\n");
>> -
>> - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev,
>> "rx");
>> - if (!sport->dma_rx_chan)
>> - dev_info(sport->port.dev, "DMA rx channel request failed, "
>> - "operating without rx DMA\n");
>> -
>> return 0;
>>
>> failed_attach_port:
>>

2020-03-24 16:33:50

by Andy Duan

[permalink] [raw]
Subject: RE: [EXT] Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

From: Michael Walle <[email protected]> Sent: Wednesday, March 25, 2020 12:18 AM
> Am 2020-03-24 17:12, schrieb Andy Duan:
> > From: Leonard Crestez <[email protected]> Sent: Tuesday, March
> > 24, 2020 11:27 PM
> >> On 06.03.2020 23:44, Michael Walle wrote:
> >> > The DMA channel might not be available at probe time. This is esp.
> >> > the case if the DMA controller has an IOMMU mapping.
> >> >
> >> > There is also another caveat. If there is no DMA controller at all,
> >> > dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot
> >> > test for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will
> >> > fail to probe if, for example, the DMA driver is not enabled in the
> >> > kernel configuration.
> > If DMA driver is not enabled, we should disable DMA controller node in
> > dts file to match current sw environment, then driver doesn't do defer
> > probe.
> >
> > So I still suggest to check -EPROBE_DEFER for
> > dma_request_slave_channel() in
> > .probe() function.
>
> I don't know if I can follow you here. This would lead to non functional setups,
> eg. one build its own kernel with DMA disabled, but still have a device tree
> with the DMA nodes. And besides, the current workaround to request the
> DMA channel in startup() is basically working, isn't it? And once the underlying
> problem is fixed (the infinite EPROBE_DEFER), it could be moved back into
> _probe().
>
> -michael

I think the user use wrong dtb file. The dtb file doesn't reflect the real enabled
modules. For such case, there have many problems for syscon,... that other modules
depends on them.

So we cannot support wrong usage cases, that is my thought.

Thanks,
Andy

>
> >
> > Andy
> >> >
> >> > To workaround this, we request the DMA channel in _startup(). Other
> >> > serial drivers do it the same way.
> >> >
> >> > Signed-off-by: Michael Walle <[email protected]>
> >>
> >> This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
> >> next-20200324 if this patch is reverted)
> >>
> >> > ---
> >> > drivers/tty/serial/fsl_lpuart.c | 88
> +++++++++++++++++++++------------
> >> > 1 file changed, 57 insertions(+), 31 deletions(-)
> >> >
> >> > diff --git a/drivers/tty/serial/fsl_lpuart.c
> >> > b/drivers/tty/serial/fsl_lpuart.c index c31b8f3db6bf..33798df4d727
> >> > 100644
> >> > --- a/drivers/tty/serial/fsl_lpuart.c
> >> > +++ b/drivers/tty/serial/fsl_lpuart.c
> >> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
> >> lpuart_port *sport)
> >> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
> >> > {
> >> > u32 uartbaud;
> >> > + int ret;
> >> >
> >> > - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
> >> > - init_waitqueue_head(&sport->dma_wait);
> >> > - sport->lpuart_dma_tx_use = true;
> >> > - if (lpuart_is_32(sport)) {
> >> > - uartbaud = lpuart32_read(&sport->port,
> UARTBAUD);
> >> > - lpuart32_write(&sport->port,
> >> > - uartbaud |
> UARTBAUD_TDMAE, UARTBAUD);
> >> > - } else {
> >> > - writeb(readb(sport->port.membase + UARTCR5)
> |
> >> > - UARTCR5_TDMAS,
> sport->port.membase + UARTCR5);
> >> > - }
> >> > + 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));
> >>
> >> It seems that this since this is called from lpuart32_startup with
> >> &sport->port.lock held and lpuart32_console_write takes the same lock
> >> it can and hang.
> >>
> >> As a workaround I can just remove this print but there are other
> >> possible error conditions in dmaengine code which can cause a printk.
> >>
> >> Maybe the port lock should only be held around register manipulation?
> >>
> >> > + sport->dma_tx_chan = NULL;
> >> > + goto err;
> >> > + }
> >> > +
> >> > + ret = lpuart_dma_tx_request(&sport->port);
> >> > + if (!ret)
> >> > + goto err;
> >>
> >> This is backwards: lpuart_dma_tx_request returns negative errno on
> >> failure.
> >>
> >> > +
> >> > + init_waitqueue_head(&sport->dma_wait);
> >> > + sport->lpuart_dma_tx_use = true; if (lpuart_is_32(sport)) {
> >> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> >> > + lpuart32_write(&sport->port,
> >> > + uartbaud | UARTBAUD_TDMAE,
> UARTBAUD);
> >> > } else {
> >> > - sport->lpuart_dma_tx_use = false;
> >> > + writeb(readb(sport->port.membase + UARTCR5) |
> >> > + UARTCR5_TDMAS, sport->port.membase +
> UARTCR5);
> >> > }
> >> > +
> >> > + return;
> >> > +
> >> > +err:
> >> > + sport->lpuart_dma_tx_use = false;
> >> > }
> >> >
> >> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
> >> > {
> >> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
> >> > - /* set Rx DMA timeout */
> >> > - sport->dma_rx_timeout =
> msecs_to_jiffies(DMA_RX_TIMEOUT);
> >> > - if (!sport->dma_rx_timeout)
> >> > - sport->dma_rx_timeout = 1;
> >> > + int ret;
> >> >
> >> > - sport->lpuart_dma_rx_use = true;
> >> > - rx_dma_timer_init(sport);
> >> > - } else {
> >> > - sport->lpuart_dma_rx_use = false;
> >> > + 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;
> >> > + goto err;
> >> > }
> >> > +
> >> > + ret = lpuart_start_rx_dma(sport); if (ret)
> >> > + goto err;
> >>
> >> This is not backwards.
> >>
> >> > +
> >> > + /* set Rx DMA timeout */
> >> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT); if
> >> > + (!sport->dma_rx_timeout)
> >> > + sport->dma_rx_timeout = 1;
> >> > +
> >> > + sport->lpuart_dma_rx_use = true; rx_dma_timer_init(sport);
> >> > +
> >> > + return;
> >> > +
> >> > +err:
> >> > + sport->lpuart_dma_rx_use = false;
> >> > }
> >> >
> >> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
> >> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port
> >> > +*sport)
> >> >
> dmaengine_terminate_all(sport->dma_tx_chan);
> >> > }
> >> > }
> >> > +
> >> > + if (sport->dma_tx_chan)
> >> > + dma_release_channel(sport->dma_tx_chan);
> >> > + if (sport->dma_rx_chan)
> >> > + dma_release_channel(sport->dma_rx_chan);
> >> > }
> >> >
> >> > static void lpuart_shutdown(struct uart_port *port) @@ -2520,16
> >> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
> >> >
> >> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
> >> >
> >> > - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev,
> >> "tx");
> >> > - if (!sport->dma_tx_chan)
> >> > - dev_info(sport->port.dev, "DMA tx channel request failed, "
> >> > - "operating without tx DMA\n");
> >> > -
> >> > - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev,
> >> "rx");
> >> > - if (!sport->dma_rx_chan)
> >> > - dev_info(sport->port.dev, "DMA rx channel request failed, "
> >> > - "operating without rx DMA\n");
> >> > -
> >> > return 0;
> >> >
> >> > failed_attach_port:
> >> >

2020-03-24 16:37:10

by Michael Walle

[permalink] [raw]
Subject: Re: [EXT] Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

Am 2020-03-24 17:28, schrieb Andy Duan:
> From: Michael Walle <[email protected]> Sent: Wednesday, March 25, 2020
> 12:18 AM
>> Am 2020-03-24 17:12, schrieb Andy Duan:
>> > From: Leonard Crestez <[email protected]> Sent: Tuesday, March
>> > 24, 2020 11:27 PM
>> >> On 06.03.2020 23:44, Michael Walle wrote:
>> >> > The DMA channel might not be available at probe time. This is esp.
>> >> > the case if the DMA controller has an IOMMU mapping.
>> >> >
>> >> > There is also another caveat. If there is no DMA controller at all,
>> >> > dma_request_chan() will also return -EPROBE_DEFER. Thus we cannot
>> >> > test for -EPROBE_DEFER in probe(). Otherwise the lpuart driver will
>> >> > fail to probe if, for example, the DMA driver is not enabled in the
>> >> > kernel configuration.
>> > If DMA driver is not enabled, we should disable DMA controller node in
>> > dts file to match current sw environment, then driver doesn't do defer
>> > probe.
>> >
>> > So I still suggest to check -EPROBE_DEFER for
>> > dma_request_slave_channel() in
>> > .probe() function.
>>
>> I don't know if I can follow you here. This would lead to non
>> functional setups,
>> eg. one build its own kernel with DMA disabled, but still have a
>> device tree
>> with the DMA nodes. And besides, the current workaround to request the
>> DMA channel in startup() is basically working, isn't it? And once the
>> underlying
>> problem is fixed (the infinite EPROBE_DEFER), it could be moved back
>> into
>> _probe().
>>
>> -michael
>
> I think the user use wrong dtb file. The dtb file doesn't reflect the
> real enabled
> modules. For such case, there have many problems for syscon,... that
> other modules
> depends on them.

But the user doesn't use the wrong dtb. I don't consider having the DMA
channels
in the dtb makes it wrong, just because DMA is not enabled in the
kernel. If
you'd follow that argument, then the dtb is also wrong if there is for
example
a crypto device, although the kernel doesn't have support for it
enabled.

-michael

>
> So we cannot support wrong usage cases, that is my thought.
>
> Thanks,
> Andy
>
>>
>> >
>> > Andy
>> >> >
>> >> > To workaround this, we request the DMA channel in _startup(). Other
>> >> > serial drivers do it the same way.
>> >> >
>> >> > Signed-off-by: Michael Walle <[email protected]>
>> >>
>> >> This appears to cause boot hangs on imx8qxp-mek (boards boots fine on
>> >> next-20200324 if this patch is reverted)
>> >>
>> >> > ---
>> >> > drivers/tty/serial/fsl_lpuart.c | 88
>> +++++++++++++++++++++------------
>> >> > 1 file changed, 57 insertions(+), 31 deletions(-)
>> >> >
>> >> > diff --git a/drivers/tty/serial/fsl_lpuart.c
>> >> > b/drivers/tty/serial/fsl_lpuart.c index c31b8f3db6bf..33798df4d727
>> >> > 100644
>> >> > --- a/drivers/tty/serial/fsl_lpuart.c
>> >> > +++ b/drivers/tty/serial/fsl_lpuart.c
>> >> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
>> >> lpuart_port *sport)
>> >> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
>> >> > {
>> >> > u32 uartbaud;
>> >> > + int ret;
>> >> >
>> >> > - if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
>> >> > - init_waitqueue_head(&sport->dma_wait);
>> >> > - sport->lpuart_dma_tx_use = true;
>> >> > - if (lpuart_is_32(sport)) {
>> >> > - uartbaud = lpuart32_read(&sport->port,
>> UARTBAUD);
>> >> > - lpuart32_write(&sport->port,
>> >> > - uartbaud |
>> UARTBAUD_TDMAE, UARTBAUD);
>> >> > - } else {
>> >> > - writeb(readb(sport->port.membase + UARTCR5)
>> |
>> >> > - UARTCR5_TDMAS,
>> sport->port.membase + UARTCR5);
>> >> > - }
>> >> > + 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));
>> >>
>> >> It seems that this since this is called from lpuart32_startup with
>> >> &sport->port.lock held and lpuart32_console_write takes the same lock
>> >> it can and hang.
>> >>
>> >> As a workaround I can just remove this print but there are other
>> >> possible error conditions in dmaengine code which can cause a printk.
>> >>
>> >> Maybe the port lock should only be held around register manipulation?
>> >>
>> >> > + sport->dma_tx_chan = NULL;
>> >> > + goto err;
>> >> > + }
>> >> > +
>> >> > + ret = lpuart_dma_tx_request(&sport->port);
>> >> > + if (!ret)
>> >> > + goto err;
>> >>
>> >> This is backwards: lpuart_dma_tx_request returns negative errno on
>> >> failure.
>> >>
>> >> > +
>> >> > + init_waitqueue_head(&sport->dma_wait);
>> >> > + sport->lpuart_dma_tx_use = true; if (lpuart_is_32(sport)) {
>> >> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> >> > + lpuart32_write(&sport->port,
>> >> > + uartbaud | UARTBAUD_TDMAE,
>> UARTBAUD);
>> >> > } else {
>> >> > - sport->lpuart_dma_tx_use = false;
>> >> > + writeb(readb(sport->port.membase + UARTCR5) |
>> >> > + UARTCR5_TDMAS, sport->port.membase +
>> UARTCR5);
>> >> > }
>> >> > +
>> >> > + return;
>> >> > +
>> >> > +err:
>> >> > + sport->lpuart_dma_tx_use = false;
>> >> > }
>> >> >
>> >> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
>> >> > {
>> >> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
>> >> > - /* set Rx DMA timeout */
>> >> > - sport->dma_rx_timeout =
>> msecs_to_jiffies(DMA_RX_TIMEOUT);
>> >> > - if (!sport->dma_rx_timeout)
>> >> > - sport->dma_rx_timeout = 1;
>> >> > + int ret;
>> >> >
>> >> > - sport->lpuart_dma_rx_use = true;
>> >> > - rx_dma_timer_init(sport);
>> >> > - } else {
>> >> > - sport->lpuart_dma_rx_use = false;
>> >> > + 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;
>> >> > + goto err;
>> >> > }
>> >> > +
>> >> > + ret = lpuart_start_rx_dma(sport); if (ret)
>> >> > + goto err;
>> >>
>> >> This is not backwards.
>> >>
>> >> > +
>> >> > + /* set Rx DMA timeout */
>> >> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT); if
>> >> > + (!sport->dma_rx_timeout)
>> >> > + sport->dma_rx_timeout = 1;
>> >> > +
>> >> > + sport->lpuart_dma_rx_use = true; rx_dma_timer_init(sport);
>> >> > +
>> >> > + return;
>> >> > +
>> >> > +err:
>> >> > + sport->lpuart_dma_rx_use = false;
>> >> > }
>> >> >
>> >> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
>> >> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port
>> >> > +*sport)
>> >> >
>> dmaengine_terminate_all(sport->dma_tx_chan);
>> >> > }
>> >> > }
>> >> > +
>> >> > + if (sport->dma_tx_chan)
>> >> > + dma_release_channel(sport->dma_tx_chan);
>> >> > + if (sport->dma_rx_chan)
>> >> > + dma_release_channel(sport->dma_rx_chan);
>> >> > }
>> >> >
>> >> > static void lpuart_shutdown(struct uart_port *port) @@ -2520,16
>> >> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
>> >> >
>> >> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
>> >> >
>> >> > - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev,
>> >> "tx");
>> >> > - if (!sport->dma_tx_chan)
>> >> > - dev_info(sport->port.dev, "DMA tx channel request failed, "
>> >> > - "operating without tx DMA\n");
>> >> > -
>> >> > - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev,
>> >> "rx");
>> >> > - if (!sport->dma_rx_chan)
>> >> > - dev_info(sport->port.dev, "DMA rx channel request failed, "
>> >> > - "operating without rx DMA\n");
>> >> > -
>> >> > return 0;
>> >> >
>> >> > failed_attach_port:
>> >> >

2020-03-24 18:48:51

by Michael Walle

[permalink] [raw]
Subject: [PATCH 1/3] tty: serial: fsl_lpuart: move dev_info_once()

Don't take the spinlock and use dev_info_once(). This may cause a hang
because the console takes this spinlock, too. Just print this info after
we've released the lock.

Fixes: 159381df1442f ("tty: serial: fsl_lpuart: fix DMA operation when using IOMMU")
Reported-by: Leonard Crestez <[email protected]>
Signed-off-by: Michael Walle <[email protected]>
---
drivers/tty/serial/fsl_lpuart.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 9c6a018b1390..960fc2658f19 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1517,9 +1517,6 @@ static void lpuart_tx_dma_startup(struct lpuart_port *sport)

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;
}
@@ -1551,9 +1548,6 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport)

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;
goto err;
}
@@ -1601,6 +1595,13 @@ static int lpuart_startup(struct uart_port *port)

spin_unlock_irqrestore(&sport->port.lock, flags);

+ if (!sport->dma_rx_chan)
+ dev_info_once(sport->port.dev,
+ "DMA rx channel request failed, operating without rx DMA\n");
+ if (!sport->dma_tx_chan)
+ dev_info_once(sport->port.dev,
+ "DMA tx channel request failed, operating without tx DMA\n");
+
return 0;
}

@@ -1653,13 +1654,20 @@ static int lpuart32_startup(struct uart_port *port)

lpuart32_setup_watermark_enable(sport);

-
lpuart_rx_dma_startup(sport);
lpuart_tx_dma_startup(sport);

lpuart32_configure(sport);

spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ if (!sport->dma_rx_chan)
+ dev_info_once(sport->port.dev,
+ "DMA rx channel request failed, operating without rx DMA\n");
+ if (!sport->dma_tx_chan)
+ dev_info_once(sport->port.dev,
+ "DMA tx channel request failed, operating without tx DMA\n");
+
return 0;
}

--
2.20.1

2020-03-24 18:49:06

by Michael Walle

[permalink] [raw]
Subject: [RFC PATCH 3/3] tty: serial: fsl_lpuart: fix possible console deadlock

If the kernel console output is on this console any
dev_{err,warn,info}() may result in a deadlock if the sport->port.lock
spinlock is already held. This is because the _console_write() try to
aquire this lock, too. Remove any error messages where the spinlock is
taken or print after the lock is released.

Reported-by: Leonard Crestez <[email protected]>
Signed-off-by: Michael Walle <[email protected]>
---
drivers/tty/serial/fsl_lpuart.c | 35 +++++++--------------------------
1 file changed, 7 insertions(+), 28 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index bbba298b68a4..0910308b38b1 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -420,7 +420,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
struct scatterlist *sgl = sport->tx_sgl;
- struct device *dev = sport->port.dev;
struct dma_chan *chan = sport->dma_tx_chan;
int ret;

@@ -442,10 +441,8 @@ static void lpuart_dma_tx(struct lpuart_port *sport)

ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
- if (!ret) {
- dev_err(dev, "DMA mapping error for TX.\n");
+ if (!ret)
return;
- }

sport->dma_tx_desc = dmaengine_prep_slave_sg(chan, sgl,
ret, DMA_MEM_TO_DEV,
@@ -453,7 +450,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
if (!sport->dma_tx_desc) {
dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
- dev_err(dev, "Cannot prepare TX slave DMA!\n");
return;
}

@@ -520,21 +516,12 @@ static int lpuart_dma_tx_request(struct uart_port *port)
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
struct dma_slave_config dma_tx_sconfig = {};
- int ret;

dma_tx_sconfig.dst_addr = lpuart_dma_datareg_addr(sport);
dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_tx_sconfig.dst_maxburst = 1;
dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
- ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
-
- if (ret) {
- dev_err(sport->port.dev,
- "DMA slave config failed, err = %d\n", ret);
- return ret;
- }
-
- return 0;
+ return dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
}

static bool lpuart_is_32(struct lpuart_port *sport)
@@ -1074,8 +1061,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)

dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
if (dmastat == DMA_ERROR) {
- dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
spin_unlock_irqrestore(&sport->port.lock, flags);
+ dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
return;
}

@@ -1179,23 +1166,17 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
nent = dma_map_sg(chan->device->dev, &sport->rx_sgl, 1,
DMA_FROM_DEVICE);
-
- if (!nent) {
- dev_err(sport->port.dev, "DMA Rx mapping error\n");
+ if (!nent)
return -EINVAL;
- }

dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_rx_sconfig.src_maxburst = 1;
dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
- ret = dmaengine_slave_config(chan, &dma_rx_sconfig);

- if (ret < 0) {
- dev_err(sport->port.dev,
- "DMA Rx slave config failed, err = %d\n", ret);
+ ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
+ if (ret < 0)
return ret;
- }

sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
sg_dma_address(&sport->rx_sgl),
@@ -1203,10 +1184,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
sport->rx_sgl.length / 2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
- if (!sport->dma_rx_desc) {
- dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
+ if (!sport->dma_rx_desc)
return -EFAULT;
- }

sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
sport->dma_rx_desc->callback_param = sport;
--
2.20.1

2020-03-24 18:49:06

by Michael Walle

[permalink] [raw]
Subject: [PATCH 2/3] tty: serial: fsl_lpuart: fix return value checking

The return value of lpuart_dma_tx_request() is a negative errno on
failure and zero on success.

Fixes: 159381df1442f ("tty: serial: fsl_lpuart: fix DMA operation when using IOMMU")
Reported-by: Leonard Crestez <[email protected]>
Signed-off-by: Michael Walle <[email protected]>
---
drivers/tty/serial/fsl_lpuart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 960fc2658f19..bbba298b68a4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1522,7 +1522,7 @@ static void lpuart_tx_dma_startup(struct lpuart_port *sport)
}

ret = lpuart_dma_tx_request(&sport->port);
- if (!ret)
+ if (ret)
goto err;

init_waitqueue_head(&sport->dma_wait);
--
2.20.1

2020-03-25 04:09:20

by Andy Duan

[permalink] [raw]
Subject: RE: [EXT] Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

From: Michael Walle <[email protected]> Sent: Wednesday, March 25, 2020 12:35 AM
> Am 2020-03-24 17:28, schrieb Andy Duan:
> > From: Michael Walle <[email protected]> Sent: Wednesday, March 25,
> 2020
> > 12:18 AM
> >> Am 2020-03-24 17:12, schrieb Andy Duan:
> >> > From: Leonard Crestez <[email protected]> Sent: Tuesday,
> >> > March 24, 2020 11:27 PM
> >> >> On 06.03.2020 23:44, Michael Walle wrote:
> >> >> > The DMA channel might not be available at probe time. This is esp.
> >> >> > the case if the DMA controller has an IOMMU mapping.
> >> >> >
> >> >> > There is also another caveat. If there is no DMA controller at
> >> >> > all,
> >> >> > dma_request_chan() will also return -EPROBE_DEFER. Thus we
> >> >> > cannot test for -EPROBE_DEFER in probe(). Otherwise the lpuart
> >> >> > driver will fail to probe if, for example, the DMA driver is not
> >> >> > enabled in the kernel configuration.
> >> > If DMA driver is not enabled, we should disable DMA controller node
> >> > in dts file to match current sw environment, then driver doesn't do
> >> > defer probe.
> >> >
> >> > So I still suggest to check -EPROBE_DEFER for
> >> > dma_request_slave_channel() in
> >> > .probe() function.
> >>
> >> I don't know if I can follow you here. This would lead to non
> >> functional setups, eg. one build its own kernel with DMA disabled,
> >> but still have a device tree with the DMA nodes. And besides, the
> >> current workaround to request the DMA channel in startup() is
> >> basically working, isn't it? And once the underlying problem is fixed
> >> (the infinite EPROBE_DEFER), it could be moved back into _probe().
> >>
> >> -michael
> >
> > I think the user use wrong dtb file. The dtb file doesn't reflect the
> > real enabled modules. For such case, there have many problems for
> > syscon,... that other modules depends on them.
>
> But the user doesn't use the wrong dtb. I don't consider having the DMA
> channels in the dtb makes it wrong, just because DMA is not enabled in the
> kernel. If you'd follow that argument, then the dtb is also wrong if there is for
> example a crypto device, although the kernel doesn't have support for it
> enabled.
>
> -michael

dma_request_chan() is not atomic context.
Even if move it into .startup(), please move it out of spinlock context.

>
> >
> > So we cannot support wrong usage cases, that is my thought.
> >
> > Thanks,
> > Andy
> >
> >>
> >> >
> >> > Andy
> >> >> >
> >> >> > To workaround this, we request the DMA channel in _startup().
> >> >> > Other serial drivers do it the same way.
> >> >> >
> >> >> > Signed-off-by: Michael Walle <[email protected]>
> >> >>
> >> >> This appears to cause boot hangs on imx8qxp-mek (boards boots fine
> >> >> on
> >> >> next-20200324 if this patch is reverted)
> >> >>
> >> >> > ---
> >> >> > drivers/tty/serial/fsl_lpuart.c | 88
> >> +++++++++++++++++++++------------
> >> >> > 1 file changed, 57 insertions(+), 31 deletions(-)
> >> >> >
> >> >> > diff --git a/drivers/tty/serial/fsl_lpuart.c
> >> >> > b/drivers/tty/serial/fsl_lpuart.c index
> >> >> > c31b8f3db6bf..33798df4d727
> >> >> > 100644
> >> >> > --- a/drivers/tty/serial/fsl_lpuart.c
> >> >> > +++ b/drivers/tty/serial/fsl_lpuart.c
> >> >> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
> >> >> lpuart_port *sport)
> >> >> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
> >> >> > {
> >> >> > u32 uartbaud;
> >> >> > + int ret;
> >> >> >
> >> >> > - if (sport->dma_tx_chan
> && !lpuart_dma_tx_request(&sport->port)) {
> >> >> > - init_waitqueue_head(&sport->dma_wait);
> >> >> > - sport->lpuart_dma_tx_use = true;
> >> >> > - if (lpuart_is_32(sport)) {
> >> >> > - uartbaud = lpuart32_read(&sport->port,
> >> UARTBAUD);
> >> >> > - lpuart32_write(&sport->port,
> >> >> > - uartbaud |
> >> UARTBAUD_TDMAE, UARTBAUD);
> >> >> > - } else {
> >> >> > - writeb(readb(sport->port.membase +
> UARTCR5)
> >> |
> >> >> > - UARTCR5_TDMAS,
> >> sport->port.membase + UARTCR5);
> >> >> > - }
> >> >> > + 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));
> >> >>
> >> >> It seems that this since this is called from lpuart32_startup with
> >> >> &sport->port.lock held and lpuart32_console_write takes the same
> >> >> lock it can and hang.
> >> >>
> >> >> As a workaround I can just remove this print but there are other
> >> >> possible error conditions in dmaengine code which can cause a printk.
> >> >>
> >> >> Maybe the port lock should only be held around register manipulation?
> >> >>
> >> >> > + sport->dma_tx_chan = NULL;
> >> >> > + goto err;
> >> >> > + }
> >> >> > +
> >> >> > + ret = lpuart_dma_tx_request(&sport->port);
> >> >> > + if (!ret)
> >> >> > + goto err;
> >> >>
> >> >> This is backwards: lpuart_dma_tx_request returns negative errno on
> >> >> failure.
> >> >>
> >> >> > +
> >> >> > + init_waitqueue_head(&sport->dma_wait);
> >> >> > + sport->lpuart_dma_tx_use = true; if (lpuart_is_32(sport)) {
> >> >> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
> >> >> > + lpuart32_write(&sport->port,
> >> >> > + uartbaud | UARTBAUD_TDMAE,
> >> UARTBAUD);
> >> >> > } else {
> >> >> > - sport->lpuart_dma_tx_use = false;
> >> >> > + writeb(readb(sport->port.membase + UARTCR5) |
> >> >> > + UARTCR5_TDMAS, sport->port.membase +
> >> UARTCR5);
> >> >> > }
> >> >> > +
> >> >> > + return;
> >> >> > +
> >> >> > +err:
> >> >> > + sport->lpuart_dma_tx_use = false;
> >> >> > }
> >> >> >
> >> >> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
> >> >> > {
> >> >> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
> >> >> > - /* set Rx DMA timeout */
> >> >> > - sport->dma_rx_timeout =
> >> msecs_to_jiffies(DMA_RX_TIMEOUT);
> >> >> > - if (!sport->dma_rx_timeout)
> >> >> > - sport->dma_rx_timeout = 1;
> >> >> > + int ret;
> >> >> >
> >> >> > - sport->lpuart_dma_rx_use = true;
> >> >> > - rx_dma_timer_init(sport);
> >> >> > - } else {
> >> >> > - sport->lpuart_dma_rx_use = false;
> >> >> > + 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;
> >> >> > + goto err;
> >> >> > }
> >> >> > +
> >> >> > + ret = lpuart_start_rx_dma(sport); if (ret)
> >> >> > + goto err;
> >> >>
> >> >> This is not backwards.
> >> >>
> >> >> > +
> >> >> > + /* set Rx DMA timeout */
> >> >> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
> if
> >> >> > + (!sport->dma_rx_timeout)
> >> >> > + sport->dma_rx_timeout = 1;
> >> >> > +
> >> >> > + sport->lpuart_dma_rx_use = true; rx_dma_timer_init(sport);
> >> >> > +
> >> >> > + return;
> >> >> > +
> >> >> > +err:
> >> >> > + sport->lpuart_dma_rx_use = false;
> >> >> > }
> >> >> >
> >> >> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
> >> >> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port
> >> >> > +*sport)
> >> >> >
> >> dmaengine_terminate_all(sport->dma_tx_chan);
> >> >> > }
> >> >> > }
> >> >> > +
> >> >> > + if (sport->dma_tx_chan)
> >> >> > + dma_release_channel(sport->dma_tx_chan);
> >> >> > + if (sport->dma_rx_chan)
> >> >> > + dma_release_channel(sport->dma_rx_chan);
> >> >> > }
> >> >> >
> >> >> > static void lpuart_shutdown(struct uart_port *port) @@
> >> >> > -2520,16
> >> >> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
> >> >> >
> >> >> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
> >> >> >
> >> >> > - sport->dma_tx_chan =
> >> >> > dma_request_slave_channel(sport->port.dev,
> >> >> "tx");
> >> >> > - if (!sport->dma_tx_chan)
> >> >> > - dev_info(sport->port.dev, "DMA tx channel request
> failed, "
> >> >> > - "operating without tx DMA\n");
> >> >> > -
> >> >> > - sport->dma_rx_chan =
> >> >> > dma_request_slave_channel(sport->port.dev,
> >> >> "rx");
> >> >> > - if (!sport->dma_rx_chan)
> >> >> > - dev_info(sport->port.dev, "DMA rx channel request
> failed, "
> >> >> > - "operating without rx DMA\n");
> >> >> > -
> >> >> > return 0;
> >> >> >
> >> >> > failed_attach_port:
> >> >> >

2020-03-25 08:32:28

by Michael Walle

[permalink] [raw]
Subject: Re: [EXT] Re: [PATCH v4 1/4] tty: serial: fsl_lpuart: fix DMA operation when using IOMMU

Hi Andy,

Am 2020-03-25 05:08, schrieb Andy Duan:
> From: Michael Walle <[email protected]> Sent: Wednesday, March 25, 2020
> 12:35 AM
>> Am 2020-03-24 17:28, schrieb Andy Duan:
>> > From: Michael Walle <[email protected]> Sent: Wednesday, March 25,
>> 2020
>> > 12:18 AM
>> >> Am 2020-03-24 17:12, schrieb Andy Duan:
>> >> > From: Leonard Crestez <[email protected]> Sent: Tuesday,
>> >> > March 24, 2020 11:27 PM
>> >> >> On 06.03.2020 23:44, Michael Walle wrote:
>> >> >> > The DMA channel might not be available at probe time. This is esp.
>> >> >> > the case if the DMA controller has an IOMMU mapping.
>> >> >> >
>> >> >> > There is also another caveat. If there is no DMA controller at
>> >> >> > all,
>> >> >> > dma_request_chan() will also return -EPROBE_DEFER. Thus we
>> >> >> > cannot test for -EPROBE_DEFER in probe(). Otherwise the lpuart
>> >> >> > driver will fail to probe if, for example, the DMA driver is not
>> >> >> > enabled in the kernel configuration.
>> >> > If DMA driver is not enabled, we should disable DMA controller node
>> >> > in dts file to match current sw environment, then driver doesn't do
>> >> > defer probe.
>> >> >
>> >> > So I still suggest to check -EPROBE_DEFER for
>> >> > dma_request_slave_channel() in
>> >> > .probe() function.
>> >>
>> >> I don't know if I can follow you here. This would lead to non
>> >> functional setups, eg. one build its own kernel with DMA disabled,
>> >> but still have a device tree with the DMA nodes. And besides, the
>> >> current workaround to request the DMA channel in startup() is
>> >> basically working, isn't it? And once the underlying problem is fixed
>> >> (the infinite EPROBE_DEFER), it could be moved back into _probe().
>> >>
>> >> -michael
>> >
>> > I think the user use wrong dtb file. The dtb file doesn't reflect the
>> > real enabled modules. For such case, there have many problems for
>> > syscon,... that other modules depends on them.
>>
>> But the user doesn't use the wrong dtb. I don't consider having the
>> DMA
>> channels in the dtb makes it wrong, just because DMA is not enabled in
>> the
>> kernel. If you'd follow that argument, then the dtb is also wrong if
>> there is for
>> example a crypto device, although the kernel doesn't have support for
>> it
>> enabled.
>>
>> -michael
>
> dma_request_chan() is not atomic context.
> Even if move it into .startup(), please move it out of spinlock
> context.

Agreed. I'll send a respin of my fixes patches later.

-michael

>
>>
>> >
>> > So we cannot support wrong usage cases, that is my thought.
>> >
>> > Thanks,
>> > Andy
>> >
>> >>
>> >> >
>> >> > Andy
>> >> >> >
>> >> >> > To workaround this, we request the DMA channel in _startup().
>> >> >> > Other serial drivers do it the same way.
>> >> >> >
>> >> >> > Signed-off-by: Michael Walle <[email protected]>
>> >> >>
>> >> >> This appears to cause boot hangs on imx8qxp-mek (boards boots fine
>> >> >> on
>> >> >> next-20200324 if this patch is reverted)
>> >> >>
>> >> >> > ---
>> >> >> > drivers/tty/serial/fsl_lpuart.c | 88
>> >> +++++++++++++++++++++------------
>> >> >> > 1 file changed, 57 insertions(+), 31 deletions(-)
>> >> >> >
>> >> >> > diff --git a/drivers/tty/serial/fsl_lpuart.c
>> >> >> > b/drivers/tty/serial/fsl_lpuart.c index
>> >> >> > c31b8f3db6bf..33798df4d727
>> >> >> > 100644
>> >> >> > --- a/drivers/tty/serial/fsl_lpuart.c
>> >> >> > +++ b/drivers/tty/serial/fsl_lpuart.c
>> >> >> > @@ -1493,36 +1493,67 @@ static void rx_dma_timer_init(struct
>> >> >> lpuart_port *sport)
>> >> >> > static void lpuart_tx_dma_startup(struct lpuart_port *sport)
>> >> >> > {
>> >> >> > u32 uartbaud;
>> >> >> > + int ret;
>> >> >> >
>> >> >> > - if (sport->dma_tx_chan
>> && !lpuart_dma_tx_request(&sport->port)) {
>> >> >> > - init_waitqueue_head(&sport->dma_wait);
>> >> >> > - sport->lpuart_dma_tx_use = true;
>> >> >> > - if (lpuart_is_32(sport)) {
>> >> >> > - uartbaud = lpuart32_read(&sport->port,
>> >> UARTBAUD);
>> >> >> > - lpuart32_write(&sport->port,
>> >> >> > - uartbaud |
>> >> UARTBAUD_TDMAE, UARTBAUD);
>> >> >> > - } else {
>> >> >> > - writeb(readb(sport->port.membase +
>> UARTCR5)
>> >> |
>> >> >> > - UARTCR5_TDMAS,
>> >> sport->port.membase + UARTCR5);
>> >> >> > - }
>> >> >> > + 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));
>> >> >>
>> >> >> It seems that this since this is called from lpuart32_startup with
>> >> >> &sport->port.lock held and lpuart32_console_write takes the same
>> >> >> lock it can and hang.
>> >> >>
>> >> >> As a workaround I can just remove this print but there are other
>> >> >> possible error conditions in dmaengine code which can cause a printk.
>> >> >>
>> >> >> Maybe the port lock should only be held around register manipulation?
>> >> >>
>> >> >> > + sport->dma_tx_chan = NULL;
>> >> >> > + goto err;
>> >> >> > + }
>> >> >> > +
>> >> >> > + ret = lpuart_dma_tx_request(&sport->port);
>> >> >> > + if (!ret)
>> >> >> > + goto err;
>> >> >>
>> >> >> This is backwards: lpuart_dma_tx_request returns negative errno on
>> >> >> failure.
>> >> >>
>> >> >> > +
>> >> >> > + init_waitqueue_head(&sport->dma_wait);
>> >> >> > + sport->lpuart_dma_tx_use = true; if (lpuart_is_32(sport)) {
>> >> >> > + uartbaud = lpuart32_read(&sport->port, UARTBAUD);
>> >> >> > + lpuart32_write(&sport->port,
>> >> >> > + uartbaud | UARTBAUD_TDMAE,
>> >> UARTBAUD);
>> >> >> > } else {
>> >> >> > - sport->lpuart_dma_tx_use = false;
>> >> >> > + writeb(readb(sport->port.membase + UARTCR5) |
>> >> >> > + UARTCR5_TDMAS, sport->port.membase +
>> >> UARTCR5);
>> >> >> > }
>> >> >> > +
>> >> >> > + return;
>> >> >> > +
>> >> >> > +err:
>> >> >> > + sport->lpuart_dma_tx_use = false;
>> >> >> > }
>> >> >> >
>> >> >> > static void lpuart_rx_dma_startup(struct lpuart_port *sport)
>> >> >> > {
>> >> >> > - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
>> >> >> > - /* set Rx DMA timeout */
>> >> >> > - sport->dma_rx_timeout =
>> >> msecs_to_jiffies(DMA_RX_TIMEOUT);
>> >> >> > - if (!sport->dma_rx_timeout)
>> >> >> > - sport->dma_rx_timeout = 1;
>> >> >> > + int ret;
>> >> >> >
>> >> >> > - sport->lpuart_dma_rx_use = true;
>> >> >> > - rx_dma_timer_init(sport);
>> >> >> > - } else {
>> >> >> > - sport->lpuart_dma_rx_use = false;
>> >> >> > + 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;
>> >> >> > + goto err;
>> >> >> > }
>> >> >> > +
>> >> >> > + ret = lpuart_start_rx_dma(sport); if (ret)
>> >> >> > + goto err;
>> >> >>
>> >> >> This is not backwards.
>> >> >>
>> >> >> > +
>> >> >> > + /* set Rx DMA timeout */
>> >> >> > + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
>> if
>> >> >> > + (!sport->dma_rx_timeout)
>> >> >> > + sport->dma_rx_timeout = 1;
>> >> >> > +
>> >> >> > + sport->lpuart_dma_rx_use = true; rx_dma_timer_init(sport);
>> >> >> > +
>> >> >> > + return;
>> >> >> > +
>> >> >> > +err:
>> >> >> > + sport->lpuart_dma_rx_use = false;
>> >> >> > }
>> >> >> >
>> >> >> > static int lpuart_startup(struct uart_port *port) @@ -1615,6
>> >> >> > +1646,11 @@ static void lpuart_dma_shutdown(struct lpuart_port
>> >> >> > +*sport)
>> >> >> >
>> >> dmaengine_terminate_all(sport->dma_tx_chan);
>> >> >> > }
>> >> >> > }
>> >> >> > +
>> >> >> > + if (sport->dma_tx_chan)
>> >> >> > + dma_release_channel(sport->dma_tx_chan);
>> >> >> > + if (sport->dma_rx_chan)
>> >> >> > + dma_release_channel(sport->dma_rx_chan);
>> >> >> > }
>> >> >> >
>> >> >> > static void lpuart_shutdown(struct uart_port *port) @@
>> >> >> > -2520,16
>> >> >> > +2556,6 @@ static int lpuart_probe(struct platform_device *pdev)
>> >> >> >
>> >> >> > sport->port.rs485_config(&sport->port, &sport->port.rs485);
>> >> >> >
>> >> >> > - sport->dma_tx_chan =
>> >> >> > dma_request_slave_channel(sport->port.dev,
>> >> >> "tx");
>> >> >> > - if (!sport->dma_tx_chan)
>> >> >> > - dev_info(sport->port.dev, "DMA tx channel request
>> failed, "
>> >> >> > - "operating without tx DMA\n");
>> >> >> > -
>> >> >> > - sport->dma_rx_chan =
>> >> >> > dma_request_slave_channel(sport->port.dev,
>> >> >> "rx");
>> >> >> > - if (!sport->dma_rx_chan)
>> >> >> > - dev_info(sport->port.dev, "DMA rx channel request
>> failed, "
>> >> >> > - "operating without rx DMA\n");
>> >> >> > -
>> >> >> > return 0;
>> >> >> >
>> >> >> > failed_attach_port:
>> >> >> >

2020-03-25 09:16:02

by Michael Walle

[permalink] [raw]
Subject: Re: [PATCH 1/3] tty: serial: fsl_lpuart: move dev_info_once()

Am 2020-03-24 19:47, schrieb Michael Walle:
> Don't take the spinlock and use dev_info_once(). This may cause a hang
> because the console takes this spinlock, too. Just print this info
> after
> we've released the lock.
>
> Fixes: 159381df1442f ("tty: serial: fsl_lpuart: fix DMA operation when
> using IOMMU")
> Reported-by: Leonard Crestez <[email protected]>
> Signed-off-by: Michael Walle <[email protected]>

Because the patch subject was renamed: This patch series is superseded
by the v2:

https://lore.kernel.org/linux-serial/[email protected]/

I didn't include the 3/3 RFC patch though. This is a bigger change,
which
should be carefully reviewed by the maintainer of the fsl_lpuart.c.

-michael


> ---
> drivers/tty/serial/fsl_lpuart.c | 22 +++++++++++++++-------
> 1 file changed, 15 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/tty/serial/fsl_lpuart.c
> b/drivers/tty/serial/fsl_lpuart.c
> index 9c6a018b1390..960fc2658f19 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -1517,9 +1517,6 @@ static void lpuart_tx_dma_startup(struct
> lpuart_port *sport)
>
> 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;
> }
> @@ -1551,9 +1548,6 @@ static void lpuart_rx_dma_startup(struct
> lpuart_port *sport)
>
> 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;
> goto err;
> }
> @@ -1601,6 +1595,13 @@ static int lpuart_startup(struct uart_port
> *port)
>
> spin_unlock_irqrestore(&sport->port.lock, flags);
>
> + if (!sport->dma_rx_chan)
> + dev_info_once(sport->port.dev,
> + "DMA rx channel request failed, operating without rx DMA\n");
> + if (!sport->dma_tx_chan)
> + dev_info_once(sport->port.dev,
> + "DMA tx channel request failed, operating without tx DMA\n");
> +
> return 0;
> }
>
> @@ -1653,13 +1654,20 @@ static int lpuart32_startup(struct uart_port
> *port)
>
> lpuart32_setup_watermark_enable(sport);
>
> -
> lpuart_rx_dma_startup(sport);
> lpuart_tx_dma_startup(sport);
>
> lpuart32_configure(sport);
>
> spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> + if (!sport->dma_rx_chan)
> + dev_info_once(sport->port.dev,
> + "DMA rx channel request failed, operating without rx DMA\n");
> + if (!sport->dma_tx_chan)
> + dev_info_once(sport->port.dev,
> + "DMA tx channel request failed, operating without tx DMA\n");
> +
> return 0;
> }

2020-03-25 12:22:37

by Leonard Crestez

[permalink] [raw]
Subject: Re: [RFC PATCH 3/3] tty: serial: fsl_lpuart: fix possible console deadlock

On 2020-03-24 8:48 PM, Michael Walle wrote:
> If the kernel console output is on this console any
> dev_{err,warn,info}() may result in a deadlock if the sport->port.lock
> spinlock is already held. This is because the _console_write() try to
> aquire this lock, too. Remove any error messages where the spinlock is
> taken or print after the lock is released.
>
> Reported-by: Leonard Crestez <[email protected]>
> Signed-off-by: Michael Walle <[email protected]>

It seems that this was an issue even before commit 159381df1442 ("tty:
serial: fsl_lpuart: fix DMA operation when using IOMMU") but these error
prints never triggered.

Would it be possible to move all the dma alloc/config/prep outside the
serial port lock? As it stands this still calls into dmaengine coode and
that might decide to print as well.

Really I don't think the lock needs to protect more than bits like
TDMAE/RDMAE.

BTW: You should add more people in CC for reviews, for example
[email protected] is checked by a lot of people.

> ---
> drivers/tty/serial/fsl_lpuart.c | 35 +++++++--------------------------
> 1 file changed, 7 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
> index bbba298b68a4..0910308b38b1 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -420,7 +420,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
> {
> struct circ_buf *xmit = &sport->port.state->xmit;
> struct scatterlist *sgl = sport->tx_sgl;
> - struct device *dev = sport->port.dev;
> struct dma_chan *chan = sport->dma_tx_chan;
> int ret;
>
> @@ -442,10 +441,8 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
>
> ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
> DMA_TO_DEVICE);
> - if (!ret) {
> - dev_err(dev, "DMA mapping error for TX.\n");
> + if (!ret)
> return;
> - }
>
> sport->dma_tx_desc = dmaengine_prep_slave_sg(chan, sgl,
> ret, DMA_MEM_TO_DEV,
> @@ -453,7 +450,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
> if (!sport->dma_tx_desc) {
> dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
> DMA_TO_DEVICE);
> - dev_err(dev, "Cannot prepare TX slave DMA!\n");
> return;
> }
>
> @@ -520,21 +516,12 @@ static int lpuart_dma_tx_request(struct uart_port *port)
> struct lpuart_port *sport = container_of(port,
> struct lpuart_port, port);
> struct dma_slave_config dma_tx_sconfig = {};
> - int ret;
>
> dma_tx_sconfig.dst_addr = lpuart_dma_datareg_addr(sport);
> dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> dma_tx_sconfig.dst_maxburst = 1;
> dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
> - ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
> -
> - if (ret) {
> - dev_err(sport->port.dev,
> - "DMA slave config failed, err = %d\n", ret);
> - return ret;
> - }
> -
> - return 0;
> + return dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
> }
>
> static bool lpuart_is_32(struct lpuart_port *sport)
> @@ -1074,8 +1061,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
>
> dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
> if (dmastat == DMA_ERROR) {
> - dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
> spin_unlock_irqrestore(&sport->port.lock, flags);
> + dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
> return;
> }
>
> @@ -1179,23 +1166,17 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
> sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
> nent = dma_map_sg(chan->device->dev, &sport->rx_sgl, 1,
> DMA_FROM_DEVICE);
> -
> - if (!nent) {
> - dev_err(sport->port.dev, "DMA Rx mapping error\n");
> + if (!nent)
> return -EINVAL;
> - }
>
> dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
> dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> dma_rx_sconfig.src_maxburst = 1;
> dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
> - ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
>
> - if (ret < 0) {
> - dev_err(sport->port.dev,
> - "DMA Rx slave config failed, err = %d\n", ret);
> + ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
> + if (ret < 0)
> return ret;
> - }
>
> sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
> sg_dma_address(&sport->rx_sgl),
> @@ -1203,10 +1184,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
> sport->rx_sgl.length / 2,
> DMA_DEV_TO_MEM,
> DMA_PREP_INTERRUPT);
> - if (!sport->dma_rx_desc) {
> - dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
> + if (!sport->dma_rx_desc)
> return -EFAULT;
> - }
>
> sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
> sport->dma_rx_desc->callback_param = sport;
>

2020-03-25 12:45:48

by Michael Walle

[permalink] [raw]
Subject: Re: [RFC PATCH 3/3] tty: serial: fsl_lpuart: fix possible console deadlock

Am 2020-03-25 13:21, schrieb Leonard Crestez:
> On 2020-03-24 8:48 PM, Michael Walle wrote:
>> If the kernel console output is on this console any
>> dev_{err,warn,info}() may result in a deadlock if the sport->port.lock
>> spinlock is already held. This is because the _console_write() try to
>> aquire this lock, too. Remove any error messages where the spinlock is
>> taken or print after the lock is released.
>>
>> Reported-by: Leonard Crestez <[email protected]>
>> Signed-off-by: Michael Walle <[email protected]>
>
> It seems that this was an issue even before commit 159381df1442 ("tty:
> serial: fsl_lpuart: fix DMA operation when using IOMMU") but these
> error
> prints never triggered.

Yeah, it just triggers because there is now a print by default (if DMA
is
not available) thus this RFC doesn't contain the Fixes: tag.

> Would it be possible to move all the dma alloc/config/prep outside the
> serial port lock? As it stands this still calls into dmaengine coode
> and
> that might decide to print as well.

TBH I don't want to refactor the whole driver. All I wanted to do was to
add LS1028A support to this driver which already resulted in a 7 patches
series with various other bugfixes. Could NXP take this over? I could
certainly do rewiews/testing on my board, though.

> Really I don't think the lock needs to protect more than bits like
> TDMAE/RDMAE.
>
> BTW: You should add more people in CC for reviews, for example
> [email protected] is checked by a lot of people.

It would be good to have N: lpuart (or fsl_lpuart?) in the
corresponding entry in MAINTAINERS, so it will automatically added.
I'll try to remember for the next patches though.

-michael
>
>> ---
>> drivers/tty/serial/fsl_lpuart.c | 35
>> +++++++--------------------------
>> 1 file changed, 7 insertions(+), 28 deletions(-)
>>
>> diff --git a/drivers/tty/serial/fsl_lpuart.c
>> b/drivers/tty/serial/fsl_lpuart.c
>> index bbba298b68a4..0910308b38b1 100644
>> --- a/drivers/tty/serial/fsl_lpuart.c
>> +++ b/drivers/tty/serial/fsl_lpuart.c
>> @@ -420,7 +420,6 @@ static void lpuart_dma_tx(struct lpuart_port
>> *sport)
>> {
>> struct circ_buf *xmit = &sport->port.state->xmit;
>> struct scatterlist *sgl = sport->tx_sgl;
>> - struct device *dev = sport->port.dev;
>> struct dma_chan *chan = sport->dma_tx_chan;
>> int ret;
>>
>> @@ -442,10 +441,8 @@ static void lpuart_dma_tx(struct lpuart_port
>> *sport)
>>
>> ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
>> DMA_TO_DEVICE);
>> - if (!ret) {
>> - dev_err(dev, "DMA mapping error for TX.\n");
>> + if (!ret)
>> return;
>> - }
>>
>> sport->dma_tx_desc = dmaengine_prep_slave_sg(chan, sgl,
>> ret, DMA_MEM_TO_DEV,
>> @@ -453,7 +450,6 @@ static void lpuart_dma_tx(struct lpuart_port
>> *sport)
>> if (!sport->dma_tx_desc) {
>> dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
>> DMA_TO_DEVICE);
>> - dev_err(dev, "Cannot prepare TX slave DMA!\n");
>> return;
>> }
>>
>> @@ -520,21 +516,12 @@ static int lpuart_dma_tx_request(struct
>> uart_port *port)
>> struct lpuart_port *sport = container_of(port,
>> struct lpuart_port, port);
>> struct dma_slave_config dma_tx_sconfig = {};
>> - int ret;
>>
>> dma_tx_sconfig.dst_addr = lpuart_dma_datareg_addr(sport);
>> dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
>> dma_tx_sconfig.dst_maxburst = 1;
>> dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
>> - ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
>> -
>> - if (ret) {
>> - dev_err(sport->port.dev,
>> - "DMA slave config failed, err = %d\n", ret);
>> - return ret;
>> - }
>> -
>> - return 0;
>> + return dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
>> }
>>
>> static bool lpuart_is_32(struct lpuart_port *sport)
>> @@ -1074,8 +1061,8 @@ static void lpuart_copy_rx_to_tty(struct
>> lpuart_port *sport)
>>
>> dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
>> if (dmastat == DMA_ERROR) {
>> - dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
>> spin_unlock_irqrestore(&sport->port.lock, flags);
>> + dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
>> return;
>> }
>>
>> @@ -1179,23 +1166,17 @@ static inline int lpuart_start_rx_dma(struct
>> lpuart_port *sport)
>> sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
>> nent = dma_map_sg(chan->device->dev, &sport->rx_sgl, 1,
>> DMA_FROM_DEVICE);
>> -
>> - if (!nent) {
>> - dev_err(sport->port.dev, "DMA Rx mapping error\n");
>> + if (!nent)
>> return -EINVAL;
>> - }
>>
>> dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
>> dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
>> dma_rx_sconfig.src_maxburst = 1;
>> dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
>> - ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
>>
>> - if (ret < 0) {
>> - dev_err(sport->port.dev,
>> - "DMA Rx slave config failed, err = %d\n", ret);
>> + ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
>> + if (ret < 0)
>> return ret;
>> - }
>>
>> sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
>> sg_dma_address(&sport->rx_sgl),
>> @@ -1203,10 +1184,8 @@ static inline int lpuart_start_rx_dma(struct
>> lpuart_port *sport)
>> sport->rx_sgl.length / 2,
>> DMA_DEV_TO_MEM,
>> DMA_PREP_INTERRUPT);
>> - if (!sport->dma_rx_desc) {
>> - dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
>> + if (!sport->dma_rx_desc)
>> return -EFAULT;
>> - }
>>
>> sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
>> sport->dma_rx_desc->callback_param = sport;
>>