|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3 2/2] arm/xen: vpl011: Fix SBSA UART interrupt assertion
On Tue, 24 Oct 2017, Andre Przywara wrote:
> From: Bhupinder Thakur <bhupinder.thakur@xxxxxxxxxx>
>
> With the current SBSA UART emulation, streaming larger amounts of data
> (as caused by "find /", for instance) can lead to character loses.
^ losses
> This is due to the OUT ring buffer getting full, because we change the
> TX interrupt bit only when the FIFO is actually full, and not already
> when it's half-way filled, as the Linux driver expects.
> The SBSA spec does not explicitly state this, but we assume that an
> SBSA compliant UART uses the PL011 default "interrupt FIFO level select
> register" value of "1/2 way". The Linux driver certainly makes this
> assumption, so it expect to be able to write a number of characters
> after the TX interrupt line has been asserted.
> On a similar issue we have the same wrong behaviour on the receive side.
> However changing the RX interrupt to trigger on reaching half of the FIFO
> level will lead to lag, because the guest would not be notified of incoming
> characters until the FIFO is half way filled. This leads to inacceptible
> lags when typing on a terminal.
> Real hardware solves this issue by using the "receive timeout
> interrupt" (RTI), which is triggered when character reception stops for
> 32 baud cycles. As we cannot and do not want to emulate any timing here,
> we slightly abuse the timeout interrupt to notify the guest of new
> characters: when a new character comes in, the RTI is asserted, when
> the FIFO is cleared, the interrupt gets cleared.
>
> So this patch changes the emulated interrupt trigger behaviour to come
> as close to real hardware as possible: the RX and TX interrupt trigger
> when the FIFO gets half full / half empty, and the RTI interrupt signals
> new incoming characters.
>
> [Andre: reword commit message, introduce receive timeout interrupt, add
> comments]
>
> Signed-off-by: Bhupinder Thakur <bhupinder.thakur@xxxxxxxxxx>
> Reviewed-by: Andre Przywara <andre.przywara@xxxxxxxxxx>
> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxxxxx>
This is good, only minor cosmetic comments.
Acked-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
Julien, can we have the release-ack?
> ---
> xen/arch/arm/vpl011.c | 131
> ++++++++++++++++++++++++++++++-------------
> xen/include/asm-arm/vpl011.h | 2 +
> 2 files changed, 94 insertions(+), 39 deletions(-)
>
> diff --git a/xen/arch/arm/vpl011.c b/xen/arch/arm/vpl011.c
> index 0b0743679f..6d02406acf 100644
> --- a/xen/arch/arm/vpl011.c
> +++ b/xen/arch/arm/vpl011.c
> @@ -18,6 +18,9 @@
>
> #define XEN_WANT_FLEX_CONSOLE_RING 1
>
> +/* We assume the PL011 default of "1/2 way" for the FIFO trigger level. */
> +#define SBSA_UART_FIFO_LEVEL (SBSA_UART_FIFO_SIZE / 2)
> +
> #include <xen/errno.h>
> #include <xen/event.h>
> #include <xen/guest_access.h>
> @@ -93,24 +96,37 @@ static uint8_t vpl011_read_data(struct domain *d)
> */
> if ( xencons_queued(in_prod, in_cons, sizeof(intf->in)) > 0 )
> {
> + unsigned int fifo_level;
> +
> data = intf->in[xencons_mask(in_cons, sizeof(intf->in))];
> in_cons += 1;
> smp_mb();
> intf->in_cons = in_cons;
> +
> + fifo_level = xencons_queued(in_prod, in_cons, sizeof(intf->in));
This is only cosmetics but can we call xencons_queued only once? See the `if'
just above.
> + /* If the FIFO is now empty, we clear the receive timeout interrupt.
> */
> + if ( fifo_level == 0 )
> + {
> + vpl011->uartfr |= RXFE;
> + vpl011->uartris &= ~RTI;
> + }
> +
> + /* If the FIFO is more than half empty, we clear the RX interrupt. */
> + if ( fifo_level < sizeof(intf->in) - SBSA_UART_FIFO_LEVEL )
> + vpl011->uartris &= ~RXI;
> +
> + vpl011_update_interrupt_status(d);
> }
> else
> gprintk(XENLOG_ERR, "vpl011: Unexpected IN ring buffer empty\n");
>
> - if ( xencons_queued(in_prod, in_cons, sizeof(intf->in)) == 0 )
> - {
> - vpl011->uartfr |= RXFE;
> - vpl011->uartris &= ~RXI;
> - }
> -
> + /*
> + * We have consumed a character or the FIFO was empty, so clear the
> + * "FIFO full" bit.
> + */
> vpl011->uartfr &= ~RXFF;
>
> - vpl011_update_interrupt_status(d);
> -
> VPL011_UNLOCK(d, flags);
>
> /*
> @@ -122,6 +138,24 @@ static uint8_t vpl011_read_data(struct domain *d)
> return data;
> }
>
> +static void vpl011_update_tx_fifo_status(struct vpl011 *vpl011,
> + unsigned int fifo_level)
> +{
> + struct xencons_interface *intf = vpl011->ring_buf;
> + unsigned int fifo_threshold = sizeof(intf->out) - SBSA_UART_FIFO_LEVEL;
> +
> + BUILD_BUG_ON(sizeof (intf->out) < SBSA_UART_FIFO_SIZE);
> +
> + /*
> + * Set the TXI bit only when there is space for fifo_size/2 bytes which
> + * is the trigger level for asserting/de-assterting the TX interrupt.
> + */
> + if ( fifo_level <= fifo_threshold )
> + vpl011->uartris |= TXI;
> + else
> + vpl011->uartris &= ~TXI;
> +}
> +
> static void vpl011_write_data(struct domain *d, uint8_t data)
> {
> unsigned long flags;
> @@ -146,33 +180,37 @@ static void vpl011_write_data(struct domain *d, uint8_t
> data)
> if ( xencons_queued(out_prod, out_cons, sizeof(intf->out)) !=
> sizeof (intf->out) )
> {
> + unsigned int fifo_level;
> +
> intf->out[xencons_mask(out_prod, sizeof(intf->out))] = data;
> out_prod += 1;
> smp_wmb();
> intf->out_prod = out_prod;
> - }
> - else
> - gprintk(XENLOG_ERR, "vpl011: Unexpected OUT ring buffer full\n");
>
> - if ( xencons_queued(out_prod, out_cons, sizeof(intf->out)) ==
> - sizeof (intf->out) )
> - {
> - vpl011->uartfr |= TXFF;
> - vpl011->uartris &= ~TXI;
> + fifo_level = xencons_queued(out_prod, out_cons, sizeof(intf->out));
Same here
> - /*
> - * This bit is set only when FIFO becomes full. This ensures that
> - * the SBSA UART driver can write the early console data as fast as
> - * possible, without waiting for the BUSY bit to get cleared before
> - * writing each byte.
> - */
> - vpl011->uartfr |= BUSY;
> + if ( fifo_level == sizeof (intf->out) )
code style
> + {
> + vpl011->uartfr |= TXFF;
> +
> + /*
> + * This bit is set only when FIFO becomes full. This ensures that
> + * the SBSA UART driver can write the early console data as fast
> as
> + * possible, without waiting for the BUSY bit to get cleared
> before
> + * writing each byte.
> + */
> + vpl011->uartfr |= BUSY;
> + }
> +
> + vpl011_update_tx_fifo_status(vpl011, fifo_level);
> +
> + vpl011_update_interrupt_status(d);
> }
> + else
> + gprintk(XENLOG_ERR, "vpl011: Unexpected OUT ring buffer full\n");
>
> vpl011->uartfr &= ~TXFE;
>
> - vpl011_update_interrupt_status(d);
> -
> VPL011_UNLOCK(d, flags);
>
> /*
> @@ -344,7 +382,7 @@ static void vpl011_data_avail(struct domain *d)
> struct vpl011 *vpl011 = &d->arch.vpl011;
> struct xencons_interface *intf = vpl011->ring_buf;
> XENCONS_RING_IDX in_cons, in_prod, out_cons, out_prod;
> - XENCONS_RING_IDX in_ring_qsize, out_ring_qsize;
> + XENCONS_RING_IDX in_fifo_level, out_fifo_level;
>
> VPL011_LOCK(d, flags);
>
> @@ -355,28 +393,41 @@ static void vpl011_data_avail(struct domain *d)
>
> smp_rmb();
>
> - in_ring_qsize = xencons_queued(in_prod,
> + in_fifo_level = xencons_queued(in_prod,
> in_cons,
> sizeof(intf->in));
>
> - out_ring_qsize = xencons_queued(out_prod,
> + out_fifo_level = xencons_queued(out_prod,
> out_cons,
> sizeof(intf->out));
>
> - /* Update the uart rx state if the buffer is not empty. */
> - if ( in_ring_qsize != 0 )
> - {
> + /**** Update the UART RX state ****/
> +
> + /* Clear the FIFO_EMPTY bit if the FIFO holds at least one character. */
> + if ( in_fifo_level > 0 )
> vpl011->uartfr &= ~RXFE;
> - if ( in_ring_qsize == sizeof(intf->in) )
> - vpl011->uartfr |= RXFF;
> +
> + /* Set the FIFO_FULL bit if the Xen buffer is full. */
> + if ( in_fifo_level == sizeof(intf->in) )
> + vpl011->uartfr |= RXFF;
> +
> + /* Assert the RX interrupt if the FIFO is more than half way filled. */
> + if ( in_fifo_level >= sizeof(intf->in) - SBSA_UART_FIFO_LEVEL )
> vpl011->uartris |= RXI;
> - }
>
> - /* Update the uart tx state if the buffer is not full. */
> - if ( out_ring_qsize != sizeof(intf->out) )
> + /*
> + * If the input queue is not empty, we assert the receive timeout
> interrupt.
> + * As we don't emulate any timing here, so we ignore the actual timeout
> + * of 32 baud cycles.
> + */
> + if ( in_fifo_level > 0 )
> + vpl011->uartris |= RTI;
> +
> + /**** Update the UART TX state ****/
> +
> + if ( out_fifo_level != sizeof(intf->out) )
> {
> vpl011->uartfr &= ~TXFF;
> - vpl011->uartris |= TXI;
>
> /*
> * Clear the BUSY bit as soon as space becomes available
> @@ -385,12 +436,14 @@ static void vpl011_data_avail(struct domain *d)
> */
> vpl011->uartfr &= ~BUSY;
>
> - if ( out_ring_qsize == 0 )
> - vpl011->uartfr |= TXFE;
> + vpl011_update_tx_fifo_status(vpl011, out_fifo_level);
> }
>
> vpl011_update_interrupt_status(d);
>
> + if ( out_fifo_level == 0 )
> + vpl011->uartfr |= TXFE;
> +
> VPL011_UNLOCK(d, flags);
> }
>
> diff --git a/xen/include/asm-arm/vpl011.h b/xen/include/asm-arm/vpl011.h
> index 1b583dac3c..db95ff822f 100644
> --- a/xen/include/asm-arm/vpl011.h
> +++ b/xen/include/asm-arm/vpl011.h
> @@ -28,6 +28,8 @@
> #define VPL011_LOCK(d,flags) spin_lock_irqsave(&(d)->arch.vpl011.lock, flags)
> #define VPL011_UNLOCK(d,flags)
> spin_unlock_irqrestore(&(d)->arch.vpl011.lock, flags)
>
> +#define SBSA_UART_FIFO_SIZE 32
> +
> struct vpl011 {
> void *ring_buf;
> struct page_info *ring_page;
> --
> 2.14.1
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |