qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] qemu-char: don't issue CHR_EVENT_OPEN in a BH


From: Michael Roth
Subject: [Qemu-devel] [PATCH] qemu-char: don't issue CHR_EVENT_OPEN in a BH
Date: Wed, 29 May 2013 17:27:03 -0500

When CHR_EVENT_OPEN was initially added, it was CHR_EVENT_RESET, and
it was issued as a bottom-half:

86e94dea5b740dad65446c857f6959eae43e0ba6

AFAICT the only reason this was ever done in a BH was because it was
initially used to to issue a CHR_EVENT_RESET when we initialized the
monitor, and we would in some cases modify the chr_write handler for
a new chardev backend *after* the site where we issued the reset
(see: 86e94d:qemu_chr_open_stdio())

So we executed the reset in a BH to ensure the chardev was fully
initialized before we executed the CHR_EVENT_RESET handler (which
generally involved printing out a greeting/prompt for the monitor).

At some point this event was renamed to CHR_EVENT_OPEN, and we've
maintained the use of this BH ever since.

However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:

 known bugs:

  QMP:
    session negotation resets with OPEN event, in some cases this
    is causing new sessions to get sporadically reset

 potential bugs:

  hw/usb/redirect.c:
    can_read handler checks for dev->parser != NULL, which may be
    true if CLOSED BH has not been executed yet. In the past, OPENED
    quiesced outstanding CLOSED events prior to us reading client
    data. If it's delayed, our check may allow reads to occur even
    though we haven't processed the OPENED event yet, and when we
    do finally get the OPENED event, our state may get reset.

  qtest.c:
    can begin session before OPENED event is processed, leading to
    a spurious reset of the system and irq_levels

  gdbstub.c:
    may start a gdb session prior to the machine being paused

To fix these, let's just drop the BH.

Since the initial reasoning for using it still applies to an extent,
work around that be deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized by setting a
'be_open_on_init' flag that gets checked toward the end of
qmp_chardev_add(). This defers delivery long enough that we can
be assured a CharDriverState is fully initialized before
CHR_EVENT_OPENED is sent.

Reported-by: Stefan Priebe <address@hidden>
Cc: address@hidden
Signed-off-by: Michael Roth <address@hidden>
---
 backends/baum.c       |    2 +-
 include/sysemu/char.h |    2 +-
 qemu-char.c           |   29 ++++++++++-------------------
 ui/console.c          |    2 +-
 ui/gtk.c              |    2 +-
 5 files changed, 14 insertions(+), 23 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 4cba79f..8384ef2 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -611,7 +611,7 @@ CharDriverState *chr_baum_init(void)
 
     qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
 
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
 
     return chr;
 
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 5e42c90..dc7a0d8 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -70,13 +70,13 @@ struct CharDriverState {
     void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
     void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
     void *opaque;
-    int idle_tag;
     char *label;
     char *filename;
     int be_open;
     int fe_open;
     int explicit_fe_open;
     int avail_connections;
+    bool be_open_on_init;
     QemuOpts *opts;
     QTAILQ_ENTRY(CharDriverState) next;
 };
diff --git a/qemu-char.c b/qemu-char.c
index 4f8382e..c553fbe 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -110,19 +110,9 @@ void qemu_chr_be_event(CharDriverState *s, int event)
     s->chr_event(s->handler_opaque, event);
 }
 
-static gboolean qemu_chr_be_generic_open_bh(gpointer opaque)
-{
-    CharDriverState *s = opaque;
-    qemu_chr_be_event(s, CHR_EVENT_OPENED);
-    s->idle_tag = 0;
-    return FALSE;
-}
-
 void qemu_chr_be_generic_open(CharDriverState *s)
 {
-    if (s->idle_tag == 0) {
-        s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s);
-    }
+    qemu_chr_be_event(s, CHR_EVENT_OPENED);
 }
 
 int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
@@ -505,7 +495,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState 
*drv)
     chr->chr_set_fe_open = NULL;
 
     /* Muxes are always open on creation */
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
 
     return chr;
 }
@@ -882,8 +872,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int 
fd_out)
     chr->chr_write = fd_chr_write;
     chr->chr_update_read_handler = fd_chr_update_read_handler;
     chr->chr_close = fd_chr_close;
-
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
 
     return chr;
 }
@@ -1594,8 +1583,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd)
     chr->chr_ioctl = pp_ioctl;
     chr->chr_close = pp_close;
     chr->opaque = drv;
-
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
 
     return chr;
 }
@@ -1880,7 +1868,7 @@ static CharDriverState *qemu_chr_open_win_path(const char 
*filename)
         g_free(chr);
         return NULL;
     }
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
     return chr;
 }
 
@@ -1980,7 +1968,7 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev 
*opts)
         g_free(chr);
         return NULL;
     }
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
     return chr;
 }
 
@@ -1994,7 +1982,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE 
fd_out)
     s->hcom = fd_out;
     chr->opaque = s;
     chr->chr_write = win_chr_write;
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
     return chr;
 }
 
@@ -3803,6 +3791,9 @@ ChardevReturn *qmp_chardev_add(const char *id, 
ChardevBackend *backend,
         chr->label = g_strdup(id);
         chr->avail_connections =
             (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
+        if (chr->be_open_on_init) {
+            qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+        }
         QTAILQ_INSERT_TAIL(&chardevs, chr, next);
         return ret;
     } else {
diff --git a/ui/console.c b/ui/console.c
index b30853f..79b5104 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1746,7 +1746,7 @@ static void text_console_do_init(CharDriverState *chr, 
DisplayState *ds)
         s->t_attrib = s->t_attrib_default;
     }
 
-    qemu_chr_be_generic_open(chr);
+    chr->be_open_on_init = true;
     if (chr->init)
         chr->init(chr);
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 52c3f95..a7603da 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1214,7 +1214,7 @@ static GSList *gd_vc_init(GtkDisplayState *s, 
VirtualConsole *vc, int index, GSL
 
     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
 
-    qemu_chr_be_generic_open(vc->chr);
+    vc->chr->be_open_on_init = true;
     if (vc->chr->init) {
         vc->chr->init(vc->chr);
     }
-- 
1.7.9.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]