2021-05-19 18:50:59

by Andrew Jeffery

[permalink] [raw]
Subject: [PATCH v2 0/2] serial: 8250: Mitigate Tx stall risk for Aspeed VUARTs

Hello,

Briefly, the series works around a hardware race condition in the Tx path for
Aspeed virtual UARTs. A write burst to THR on the APB interface may provoke a
transfer stall where LSR[DR] on the LPC interface remains clear despite the
presence of data in the Rx FIFO.

For the work-around patch, v2 addresses the request for a comment about the use
of serial_in():

https://lore.kernel.org/lkml/[email protected]/T/#md75702fbc3704bd4b375f1251a1415bcddea26a3

The second patch addresses the request for use of BIT() instead of an explicit
shift by converting all of the UART_{CAP,BUG}_* macros.

Please review!

Andrew

Andrew Jeffery (2):
serial: 8250: Add UART_BUG_TXRACE workaround for Aspeed VUART
serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*

drivers/tty/serial/8250/8250.h | 32 +++++++++++----------
drivers/tty/serial/8250/8250_aspeed_vuart.c | 1 +
drivers/tty/serial/8250/8250_port.c | 10 +++++++
3 files changed, 28 insertions(+), 15 deletions(-)

--
2.30.2



2021-05-19 18:51:18

by Andrew Jeffery

[permalink] [raw]
Subject: [PATCH v2 2/2] serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*

BIT(x) improves readability and safety with respect to shifts.

Signed-off-by: Andrew Jeffery <[email protected]>
---
drivers/tty/serial/8250/8250.h | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 34aa2714f3c9..4fbf1088fad8 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -7,6 +7,7 @@
* Copyright (C) 2001 Russell King.
*/

+#include <linux/bitops.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
#include <linux/dmaengine.h>
@@ -70,25 +71,25 @@ struct serial8250_config {
unsigned int flags;
};

-#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
-#define UART_CAP_EFR (1 << 9) /* UART has EFR */
-#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
-#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
-#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
-#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
-#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
-#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
-#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
-#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
+#define UART_CAP_FIFO BIT(8) /* UART has FIFO */
+#define UART_CAP_EFR BIT(9) /* UART has EFR */
+#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */
+#define UART_CAP_AFE BIT(11) /* MCR-based hw flow control */
+#define UART_CAP_UUE BIT(12) /* UART needs IER bit 6 set (Xscale) */
+#define UART_CAP_RTOIE BIT(13) /* UART needs IER bit 4 set (Xscale, Tegra) */
+#define UART_CAP_HFIFO BIT(14) /* UART has a "hidden" FIFO */
+#define UART_CAP_RPM BIT(15) /* Runtime PM is active while idle */
+#define UART_CAP_IRDA BIT(16) /* UART supports IrDA line discipline */
+#define UART_CAP_MINI BIT(17) /* Mini UART on BCM283X family lacks:
* STOP PARITY EPAR SPAR WLEN5 WLEN6
*/

-#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
-#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
-#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */
-#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */
-#define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */
-#define UART_BUG_TXRACE (1 << 5) /* UART Tx fails to set remote DR */
+#define UART_BUG_QUOT BIT(0) /* UART has buggy quot LSB */
+#define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */
+#define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */
+#define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */
+#define UART_BUG_PARITY BIT(4) /* UART mishandles parity if FIFO enabled */
+#define UART_BUG_TXRACE BIT(5) /* UART Tx fails to set remote DR */


#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
--
2.30.2


2021-05-19 19:18:15

by Andrew Jeffery

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*



On Wed, 19 May 2021, at 16:02, Jiri Slaby wrote:
> On 19. 05. 21, 8:27, Andrew Jeffery wrote:
> >
> >
> > On Wed, 19 May 2021, at 15:44, Jiri Slaby wrote:
> >> On 19. 05. 21, 2:07, Andrew Jeffery wrote:
> >>> BIT(x) improves readability and safety with respect to shifts.
> >>>
> >>> Signed-off-by: Andrew Jeffery <[email protected]>
> >>> ---
> >>> drivers/tty/serial/8250/8250.h | 33 +++++++++++++++++----------------
> >>> 1 file changed, 17 insertions(+), 16 deletions(-)
> >>>
> >>> diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
> >>> index 34aa2714f3c9..4fbf1088fad8 100644
> >>> --- a/drivers/tty/serial/8250/8250.h
> >>> +++ b/drivers/tty/serial/8250/8250.h
> >>> @@ -7,6 +7,7 @@
> >>> * Copyright (C) 2001 Russell King.
> >>> */
> >>>
> >>> +#include <linux/bitops.h>
> >>> #include <linux/serial_8250.h>
> >>> #include <linux/serial_reg.h>
> >>> #include <linux/dmaengine.h>
> >>> @@ -70,25 +71,25 @@ struct serial8250_config {
> >>> unsigned int flags;
> >>> };
> >>>
> >>> -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
> >>> -#define UART_CAP_EFR (1 << 9) /* UART has EFR */
> >>> -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
> >>> -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
> >>> -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
> >>> -#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
> >>> -#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
> >>> -#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
> >>> -#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
> >>> -#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
> >>> +#define UART_CAP_FIFO BIT(8) /* UART has FIFO */
> >>> +#define UART_CAP_EFR BIT(9) /* UART has EFR */
> >>> +#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */
> >>
> >>
> >> Perfect, except the include: BIT is not defined in bitops.h, but in
> >> bits.h (which includes vdso/bits.h). In fact, bitops.h includes bits.h
> >> too, but it's superfluous to include all those bitops.
> >
> > Maybe the recommendation in the checkpatch documentation should be
> > fixed then?
>
> +1 since:
>
> commit 8bd9cb51daac89337295b6f037b0486911e1b408
> Author: Will Deacon <[email protected]>
> Date: Tue Jun 19 13:53:08 2018 +0100
>
> locking/atomics, asm-generic: Move some macros from
> <linux/bitops.h> to a new <linux/bits.h> file
>
> So care to fix checkpatch too :)?

Yeah, I'll sort that out.

Andrew

2021-05-19 19:19:24

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*

On 19. 05. 21, 2:07, Andrew Jeffery wrote:
> BIT(x) improves readability and safety with respect to shifts.
>
> Signed-off-by: Andrew Jeffery <[email protected]>
> ---
> drivers/tty/serial/8250/8250.h | 33 +++++++++++++++++----------------
> 1 file changed, 17 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
> index 34aa2714f3c9..4fbf1088fad8 100644
> --- a/drivers/tty/serial/8250/8250.h
> +++ b/drivers/tty/serial/8250/8250.h
> @@ -7,6 +7,7 @@
> * Copyright (C) 2001 Russell King.
> */
>
> +#include <linux/bitops.h>
> #include <linux/serial_8250.h>
> #include <linux/serial_reg.h>
> #include <linux/dmaengine.h>
> @@ -70,25 +71,25 @@ struct serial8250_config {
> unsigned int flags;
> };
>
> -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
> -#define UART_CAP_EFR (1 << 9) /* UART has EFR */
> -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
> -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
> -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
> -#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
> -#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
> -#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
> -#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
> -#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
> +#define UART_CAP_FIFO BIT(8) /* UART has FIFO */
> +#define UART_CAP_EFR BIT(9) /* UART has EFR */
> +#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */


Perfect, except the include: BIT is not defined in bitops.h, but in
bits.h (which includes vdso/bits.h). In fact, bitops.h includes bits.h
too, but it's superfluous to include all those bitops.

thanks,
--
--
js
suse labs

2021-05-19 19:19:56

by Andrew Jeffery

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*



On Wed, 19 May 2021, at 15:44, Jiri Slaby wrote:
> On 19. 05. 21, 2:07, Andrew Jeffery wrote:
> > BIT(x) improves readability and safety with respect to shifts.
> >
> > Signed-off-by: Andrew Jeffery <[email protected]>
> > ---
> > drivers/tty/serial/8250/8250.h | 33 +++++++++++++++++----------------
> > 1 file changed, 17 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
> > index 34aa2714f3c9..4fbf1088fad8 100644
> > --- a/drivers/tty/serial/8250/8250.h
> > +++ b/drivers/tty/serial/8250/8250.h
> > @@ -7,6 +7,7 @@
> > * Copyright (C) 2001 Russell King.
> > */
> >
> > +#include <linux/bitops.h>
> > #include <linux/serial_8250.h>
> > #include <linux/serial_reg.h>
> > #include <linux/dmaengine.h>
> > @@ -70,25 +71,25 @@ struct serial8250_config {
> > unsigned int flags;
> > };
> >
> > -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
> > -#define UART_CAP_EFR (1 << 9) /* UART has EFR */
> > -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
> > -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
> > -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
> > -#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
> > -#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
> > -#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
> > -#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
> > -#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
> > +#define UART_CAP_FIFO BIT(8) /* UART has FIFO */
> > +#define UART_CAP_EFR BIT(9) /* UART has EFR */
> > +#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */
>
>
> Perfect, except the include: BIT is not defined in bitops.h, but in
> bits.h (which includes vdso/bits.h). In fact, bitops.h includes bits.h
> too, but it's superfluous to include all those bitops.

Maybe the recommendation in the checkpatch documentation should be
fixed then?

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/dev-tools/checkpatch.rst?h=v5.13-rc2#n473

I didn't dig through the include maze to optimise my choice.

That said, I will switch to bits.h based on your feedback above.

Thanks,

Andrew

2021-05-19 19:19:59

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] serial: 8250: Use BIT(x) for UART_{CAP,BUG}_*

On 19. 05. 21, 8:27, Andrew Jeffery wrote:
>
>
> On Wed, 19 May 2021, at 15:44, Jiri Slaby wrote:
>> On 19. 05. 21, 2:07, Andrew Jeffery wrote:
>>> BIT(x) improves readability and safety with respect to shifts.
>>>
>>> Signed-off-by: Andrew Jeffery <[email protected]>
>>> ---
>>> drivers/tty/serial/8250/8250.h | 33 +++++++++++++++++----------------
>>> 1 file changed, 17 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
>>> index 34aa2714f3c9..4fbf1088fad8 100644
>>> --- a/drivers/tty/serial/8250/8250.h
>>> +++ b/drivers/tty/serial/8250/8250.h
>>> @@ -7,6 +7,7 @@
>>> * Copyright (C) 2001 Russell King.
>>> */
>>>
>>> +#include <linux/bitops.h>
>>> #include <linux/serial_8250.h>
>>> #include <linux/serial_reg.h>
>>> #include <linux/dmaengine.h>
>>> @@ -70,25 +71,25 @@ struct serial8250_config {
>>> unsigned int flags;
>>> };
>>>
>>> -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
>>> -#define UART_CAP_EFR (1 << 9) /* UART has EFR */
>>> -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
>>> -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
>>> -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
>>> -#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
>>> -#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
>>> -#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
>>> -#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
>>> -#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
>>> +#define UART_CAP_FIFO BIT(8) /* UART has FIFO */
>>> +#define UART_CAP_EFR BIT(9) /* UART has EFR */
>>> +#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */
>>
>>
>> Perfect, except the include: BIT is not defined in bitops.h, but in
>> bits.h (which includes vdso/bits.h). In fact, bitops.h includes bits.h
>> too, but it's superfluous to include all those bitops.
>
> Maybe the recommendation in the checkpatch documentation should be
> fixed then?

+1 since:

commit 8bd9cb51daac89337295b6f037b0486911e1b408
Author: Will Deacon <[email protected]>
Date: Tue Jun 19 13:53:08 2018 +0100

locking/atomics, asm-generic: Move some macros from
<linux/bitops.h> to a new <linux/bits.h> file

So care to fix checkpatch too :)?

thanks,
--
js
suse labs

2021-05-19 21:10:12

by Andrew Jeffery

[permalink] [raw]
Subject: [PATCH v2 1/2] serial: 8250: Add UART_BUG_TXRACE workaround for Aspeed VUART

Aspeed Virtual UARTs directly bridge e.g. the system console UART on the
LPC bus to the UART interface on the BMC's internal APB. As such there's
no RS-232 signalling involved - the UART interfaces on each bus are
directly connected as the producers and consumers of the one set of
FIFOs.

The APB in the AST2600 generally runs at 100MHz while the LPC bus peaks
at 33MHz. The difference in clock speeds exposes a race in the VUART
design where a Tx data burst on the APB interface can result in a byte
lost on the LPC interface. The symptom is LSR[DR] remains clear on the
LPC interface despite data being present in its Rx FIFO, while LSR[THRE]
remains clear on the APB interface as the host has not consumed the data
the BMC has transmitted. In this state, the UART has stalled and no
further data can be transmitted without manual intervention (e.g.
resetting the FIFOs, resulting in loss of data).

The recommended work-around is to insert a read cycle on the APB
interface between writes to THR.

Cc: ChiaWei Wang <[email protected]>
Signed-off-by: Andrew Jeffery <[email protected]>
---
drivers/tty/serial/8250/8250.h | 1 +
drivers/tty/serial/8250/8250_aspeed_vuart.c | 1 +
drivers/tty/serial/8250/8250_port.c | 10 ++++++++++
3 files changed, 12 insertions(+)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 52bb21205bb6..34aa2714f3c9 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -88,6 +88,7 @@ struct serial8250_config {
#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */
#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */
#define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */
+#define UART_BUG_TXRACE (1 << 5) /* UART Tx fails to set remote DR */


#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
index a28a394ba32a..4caab8714e2c 100644
--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -440,6 +440,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev)
port.port.status = UPSTAT_SYNC_FIFO;
port.port.dev = &pdev->dev;
port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
+ port.bugs |= UART_BUG_TXRACE;

rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
if (rc < 0)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index d45dab1ab316..9d44b2b2ff18 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1809,6 +1809,16 @@ void serial8250_tx_chars(struct uart_8250_port *up)
count = up->tx_loadsz;
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ if (up->bugs & UART_BUG_TXRACE) {
+ /* The Aspeed BMC virtual UARTs have a bug where data
+ * may get stuck in the BMC's Tx FIFO from bursts of
+ * writes on the APB interface.
+ *
+ * Delay back-to-back writes by a read cycle to avoid
+ * stalling the VUART.
+ */
+ (void)serial_in(up, UART_SCR);
+ }
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_empty(xmit))
--
2.30.2