git: 39d6144dcd94 - main - ns8250_drain: Drain without DELAY first

From: Colin Percival <cperciva_at_FreeBSD.org>
Date: Sat, 08 Oct 2022 00:56:31 UTC
The branch main has been updated by cperciva:

URL: https://cgit.FreeBSD.org/src/commit/?id=39d6144dcd94e9d07f07cb554aecb1f2d4089f6d

commit 39d6144dcd94e9d07f07cb554aecb1f2d4089f6d
Author:     Colin Percival <cperciva@FreeBSD.org>
AuthorDate: 2022-08-12 23:45:17 +0000
Commit:     Colin Percival <cperciva@FreeBSD.org>
CommitDate: 2022-10-08 00:51:54 +0000

    ns8250_drain: Drain without DELAY first
    
    In virtual machines with virtual UARTs which have fictitious baud
    rates, it may be possible to drain the receive queue very quickly,
    without needing to DELAY after each character.  Attempt to read
    (and discard) the receive queue as fast as possible, stopping for
    a DELAY only when LSR_RXRDY is no longer asserted; assume that we
    have finished draining the queue when LSR_RXRDY is asserted both
    before and after a DELAY.
    
    This speeds up the boot process in FreeBSD/Firecracker by 27 ms.
    
    Reviewed by:    imp, jrtc27
    Sponsored by:   https://www.patreon.com/cperciva
    Differential Revision:  https://reviews.freebsd.org/D36184
---
 sys/dev/uart/uart_dev_ns8250.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c
index ada60b62b633..182831da7611 100644
--- a/sys/dev/uart/uart_dev_ns8250.c
+++ b/sys/dev/uart/uart_dev_ns8250.c
@@ -178,11 +178,18 @@ ns8250_drain(struct uart_bas *bas, int what)
 		 * limit high enough to handle large FIFOs and integrated
 		 * UARTs. The HP rx2600 for example has 3 UARTs on the
 		 * management board that tend to get a lot of data send
-		 * to it when the UART is first activated.
+		 * to it when the UART is first activated.  Assume that we
+		 * have finished draining if LSR_RXRDY is not asserted both
+		 * prior to and after a DELAY; but as long as LSR_RXRDY is
+		 * asserted, read (and discard) characters as quickly as
+		 * possible.
 		 */
 		limit=10*4096;
-		while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) {
-			(void)uart_getreg(bas, REG_DATA);
+		while (limit && (uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) {
+			do {
+				(void)uart_getreg(bas, REG_DATA);
+				uart_barrier(bas);
+			} while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit);
 			uart_barrier(bas);
 			DELAY(delay << 2);
 		}