9ae3a8
From 1b37b298fc1f0d69e24229191e4bbe741e4d96ab Mon Sep 17 00:00:00 2001
9ae3a8
From: Fam Zheng <famz@redhat.com>
9ae3a8
Date: Thu, 18 May 2017 09:21:25 +0200
9ae3a8
Subject: [PATCH 12/18] serial: clean up THRE/TEMT handling
9ae3a8
9ae3a8
RH-Author: Fam Zheng <famz@redhat.com>
9ae3a8
Message-id: <20170518092131.16571-13-famz@redhat.com>
9ae3a8
Patchwork-id: 75303
9ae3a8
O-Subject: [RHEL-7.4 qemu-kvm PATCH v3 12/18] serial: clean up THRE/TEMT handling
9ae3a8
Bugzilla: 1451470
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
9ae3a8
9ae3a8
From: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
- assert TEMT is cleared before sending a character; we'll get one from
9ae3a8
TSR if tsr_retry > 0, from the FIFO or THR otherwise
9ae3a8
9ae3a8
- assert THRE cleared and FIFO not empty (if enabled) before fetching a
9ae3a8
character to send.  This effectively reverts dffacd46, but the check
9ae3a8
makes no sense and commit f702e62 (serial: change retry logic to avoid
9ae3a8
concurrency, 2014-07-11) must have made it unnecessary.  The commit
9ae3a8
message for f702e62 talks about multiple calls to qemu_chr_fe_add_watch
9ae3a8
triggering s->tsr_retry >= MAX_XMIT_RETRY, but other failures were
9ae3a8
possible.  For example, if you have multiple calls, the subsequent ones
9ae3a8
will see s->tsr_retry == 0 and will find THRE and/or TEMT on entry.
9ae3a8
9ae3a8
- for clarity, raise THRI immediately after the code sets THRE
9ae3a8
9ae3a8
- check THRE to see if another character has to be sent.  This makes
9ae3a8
the assertions more obvious and also means TEMT has to be set as soon as
9ae3a8
the loop ends.  It makes the loop send both TSR and THR if flow-control
9ae3a8
happens in non-FIFO mode.  Previously, THR would be lost.
9ae3a8
9ae3a8
- clear TEMT together with THRE even in the non-FIFO case
9ae3a8
9ae3a8
The last two items are bugfixes, but they were just found by inspection
9ae3a8
and do not squash known bugs.
9ae3a8
9ae3a8
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
9ae3a8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
(cherry picked from commit 0d931d706266d6ada3bf22d3afca1afdc8d12fa9)
9ae3a8
Signed-off-by: Fam Zheng <famz@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
Conflicts:
9ae3a8
	hw/char/serial.c
9ae3a8
9ae3a8
Contextual conflict because upstream has the new timer API
9ae3a8
qemu_clock_get_ns, but downstream still uses qemu_get_clock_ns.
9ae3a8
---
9ae3a8
 hw/char/serial.c | 26 ++++++++++++--------------
9ae3a8
 1 file changed, 12 insertions(+), 14 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/char/serial.c b/hw/char/serial.c
9ae3a8
index 15c628f..c2be4bd 100644
9ae3a8
--- a/hw/char/serial.c
9ae3a8
+++ b/hw/char/serial.c
9ae3a8
@@ -224,21 +224,23 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
9ae3a8
     SerialState *s = opaque;
9ae3a8
 
9ae3a8
     do {
9ae3a8
+        assert(!(s->lsr & UART_LSR_TEMT));
9ae3a8
         if (s->tsr_retry <= 0) {
9ae3a8
+            assert(!(s->lsr & UART_LSR_THRE));
9ae3a8
+
9ae3a8
             if (s->fcr & UART_FCR_FE) {
9ae3a8
-                if (fifo8_is_empty(&s->xmit_fifo)) {
9ae3a8
-                    return FALSE;
9ae3a8
-                }
9ae3a8
+                assert(!fifo8_is_empty(&s->xmit_fifo));
9ae3a8
                 s->tsr = fifo8_pop(&s->xmit_fifo);
9ae3a8
                 if (!s->xmit_fifo.num) {
9ae3a8
                     s->lsr |= UART_LSR_THRE;
9ae3a8
                 }
9ae3a8
-            } else if ((s->lsr & UART_LSR_THRE)) {
9ae3a8
-                return FALSE;
9ae3a8
             } else {
9ae3a8
                 s->tsr = s->thr;
9ae3a8
                 s->lsr |= UART_LSR_THRE;
9ae3a8
-                s->lsr &= ~UART_LSR_TEMT;
9ae3a8
+            }
9ae3a8
+            if ((s->lsr & UART_LSR_THRE) && !s->thr_ipending) {
9ae3a8
+                s->thr_ipending = 1;
9ae3a8
+                serial_update_irq(s);
9ae3a8
             }
9ae3a8
         }
9ae3a8
 
9ae3a8
@@ -256,17 +258,13 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
9ae3a8
         } else {
9ae3a8
             s->tsr_retry = 0;
9ae3a8
         }
9ae3a8
+
9ae3a8
         /* Transmit another byte if it is already available. It is only
9ae3a8
            possible when FIFO is enabled and not empty. */
9ae3a8
-    } while ((s->fcr & UART_FCR_FE) && !fifo8_is_empty(&s->xmit_fifo));
9ae3a8
+    } while (!(s->lsr & UART_LSR_THRE));
9ae3a8
 
9ae3a8
     s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
9ae3a8
-
9ae3a8
-    if (s->lsr & UART_LSR_THRE) {
9ae3a8
-        s->lsr |= UART_LSR_TEMT;
9ae3a8
-        s->thr_ipending = 1;
9ae3a8
-        serial_update_irq(s);
9ae3a8
-    }
9ae3a8
+    s->lsr |= UART_LSR_TEMT;
9ae3a8
 
9ae3a8
     return FALSE;
9ae3a8
 }
9ae3a8
@@ -293,10 +291,10 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
                     fifo8_pop(&s->xmit_fifo);
9ae3a8
                 }
9ae3a8
                 fifo8_push(&s->xmit_fifo, s->thr);
9ae3a8
-                s->lsr &= ~UART_LSR_TEMT;
9ae3a8
             }
9ae3a8
             s->thr_ipending = 0;
9ae3a8
             s->lsr &= ~UART_LSR_THRE;
9ae3a8
+            s->lsr &= ~UART_LSR_TEMT;
9ae3a8
             serial_update_irq(s);
9ae3a8
             if (s->tsr_retry <= 0) {
9ae3a8
                 serial_xmit(NULL, G_IO_OUT, s);
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8