From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kirill Batuzov <batuzovk@ispras.ru>
Subject: [Qemu-devel] [PULL 3/5] serial: change retry logic to avoid concurrency
Date: Mon, 14 Jul 2014 17:49:26 +0200 [thread overview]
Message-ID: <1405352968-3155-4-git-send-email-pbonzini@redhat.com> (raw)
In-Reply-To: <1405352968-3155-1-git-send-email-pbonzini@redhat.com>
From: Kirill Batuzov <batuzovk@ispras.ru>
Whenever serial_xmit fails to transmit a byte it adds a watch that would
call it again when the "line" becomes ready. This results in a retry
chain:
serial_xmit -> add_watch -> serial_xmit
Each chain is able to transmit one character, and for every character
passed to serial by the guest driver a new chain is spawned.
The problem lays with the fact that a new chain is spawned even when
there is one already waiting on the watch. So there can be several retry
chains waiting concurrently on one "line". Every chain tries to transmit
current character, so character order is not messed up. But also every
chain increases retry counter (tsr_retry). If there are enough
concurrent chains this counter will hit MAX_XMIT_RETRY value and
the character will be dropped.
To reproduce this bug you need to feed serial output to some program
consuming it slowly enough. A python script from bug #1335444
description is an example of such program.
This commit changes retry logic in the following way to avoid
concurrency: instead of spawning a new chain for each character being
transmitted spawn only one and make it transmit characters until FIFO is
empty.
The change consists of two parts:
- add a do {} while () loop in serial_xmit (diff is a bit erratic
for this part, diff -w will show actual change),
- do not call serial_xmit from serial_ioport_write if there is one
waiting on the watch already.
This should fix another issue causing bug #1335444.
Signed-off-by: Kirill Batuzov <batuzovk@ispras.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/char/serial.c | 59 +++++++++++++++++++++++++++++++-------------------------
1 file changed, 33 insertions(+), 26 deletions(-)
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 54180a9..764e184 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -223,37 +223,42 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
{
SerialState *s = opaque;
- if (s->tsr_retry <= 0) {
- if (s->fcr & UART_FCR_FE) {
- if (fifo8_is_empty(&s->xmit_fifo)) {
+ do {
+ if (s->tsr_retry <= 0) {
+ if (s->fcr & UART_FCR_FE) {
+ if (fifo8_is_empty(&s->xmit_fifo)) {
+ return FALSE;
+ }
+ s->tsr = fifo8_pop(&s->xmit_fifo);
+ if (!s->xmit_fifo.num) {
+ s->lsr |= UART_LSR_THRE;
+ }
+ } else if ((s->lsr & UART_LSR_THRE)) {
return FALSE;
- }
- s->tsr = fifo8_pop(&s->xmit_fifo);
- if (!s->xmit_fifo.num) {
+ } else {
+ s->tsr = s->thr;
s->lsr |= UART_LSR_THRE;
+ s->lsr &= ~UART_LSR_TEMT;
}
- } else if ((s->lsr & UART_LSR_THRE)) {
- return FALSE;
- } else {
- s->tsr = s->thr;
- s->lsr |= UART_LSR_THRE;
- s->lsr &= ~UART_LSR_TEMT;
}
- }
- if (s->mcr & UART_MCR_LOOP) {
- /* in loopback mode, say that we just received a char */
- serial_receive1(s, &s->tsr, 1);
- } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
- if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
- qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, serial_xmit, s) > 0) {
- s->tsr_retry++;
- return FALSE;
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback mode, say that we just received a char */
+ serial_receive1(s, &s->tsr, 1);
+ } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
+ if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
+ qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
+ serial_xmit, s) > 0) {
+ s->tsr_retry++;
+ return FALSE;
+ }
+ s->tsr_retry = 0;
+ } else {
+ s->tsr_retry = 0;
}
- s->tsr_retry = 0;
- } else {
- s->tsr_retry = 0;
- }
+ /* Transmit another byte if it is already available. It is only
+ possible when FIFO is enabled and not empty. */
+ } while ((s->fcr & UART_FCR_FE) && !fifo8_is_empty(&s->xmit_fifo));
s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -293,7 +298,9 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
s->thr_ipending = 0;
s->lsr &= ~UART_LSR_THRE;
serial_update_irq(s);
- serial_xmit(NULL, G_IO_OUT, s);
+ if (s->tsr_retry <= 0) {
+ serial_xmit(NULL, G_IO_OUT, s);
+ }
}
break;
case 1:
--
1.9.3
next prev parent reply other threads:[~2014-07-14 15:49 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-07-14 15:49 [Qemu-devel] [PULL for-2.1 0/5] Serial + SCSI fixes for 2014-07-14 Paolo Bonzini
2014-07-14 15:49 ` [Qemu-devel] [PULL 1/5] scsi: Report error when lun number is in use Paolo Bonzini
2014-07-14 15:49 ` [Qemu-devel] [PULL 2/5] qemu-char: fix deadlock with "-monitor pty" Paolo Bonzini
2014-07-14 15:49 ` Paolo Bonzini [this message]
2014-07-24 12:57 ` [Qemu-devel] [PULL 3/5] serial: change retry logic to avoid concurrency Pavel Hrdina
2014-07-24 14:10 ` Paolo Bonzini
2014-07-14 15:49 ` [Qemu-devel] [PULL 4/5] virtio-scsi: fix with -M pc-i440fx-2.0 Paolo Bonzini
2014-07-14 15:49 ` [Qemu-devel] [PULL 5/5] serial-pci: remove memory regions from BAR before destroying them Paolo Bonzini
2014-07-15 13:14 ` [Qemu-devel] [PULL for-2.1 0/5] Serial + SCSI fixes for 2014-07-14 Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1405352968-3155-4-git-send-email-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=batuzovk@ispras.ru \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).