diff mbox

[PATCHv3,RFC] qemu-kvm: stop devices on vmstop

Message ID 20101121191641.GA28858@redhat.com
State New
Headers show

Commit Message

Michael S. Tsirkin Nov. 21, 2010, 7:16 p.m. UTC
Stop running devices on vmstop, so that VM does not interact with
outside world at that time.

Whitelist system handlers which run even when VM is stopped.
These are specific handlers like monitor, gdbstub, migration.
I'm not really sure about ui: spice and vnc: do they need to run?

Untested.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---

Change from previous versions:

reversed the approach, block all handlers except very specific ones.


 aio.c                  |   32 ++++++++-
 cmd.c                  |    8 +-
 gdbstub.c              |    2 +-
 migration-exec.c       |    4 +-
 migration-fd.c         |    4 +-
 migration-tcp.c        |    8 +-
 migration-unix.c       |    8 +-
 migration.c            |    8 +-
 qemu-aio.h             |    7 ++
 qemu-char.c            |   10 +++
 qemu-char.h            |    8 ++
 qemu-kvm.c             |    2 +-
 qemu-tool.c            |    9 +++
 ui/spice-core.c        |    2 +-
 ui/vnc-auth-sasl.c     |    2 +-
 ui/vnc-auth-vencrypt.c |    6 +-
 ui/vnc.c               |   14 ++--
 vl.c                   |  174 +++++++++++++++++++++++++++++++-----------------
 18 files changed, 210 insertions(+), 98 deletions(-)

Comments

Gerd Hoffmann Nov. 22, 2010, 9:57 a.m. UTC | #1
On 11/21/10 20:16, Michael S. Tsirkin wrote:
> Stop running devices on vmstop, so that VM does not interact with
> outside world at that time.
>
> Whitelist system handlers which run even when VM is stopped.
> These are specific handlers like monitor, gdbstub, migration.
> I'm not really sure about ui: spice and vnc: do they need to run?

Yes, vnc and spice should run.  They serve the clients.  They also don't 
change device state.

cheers,
   Gerd

PS: Well, in the spice case this isn't 100% true, the handlers can 
change qxl device state in some cases.  But spice has a start/stop 
handler which takes care to flush all outstanding work which could 
change device state on vmstop, so it is save to keep the handlers active 
when the vm is in stopped state.
Michael S. Tsirkin Nov. 22, 2010, 10:15 a.m. UTC | #2
On Mon, Nov 22, 2010 at 10:57:34AM +0100, Gerd Hoffmann wrote:
> On 11/21/10 20:16, Michael S. Tsirkin wrote:
> >Stop running devices on vmstop, so that VM does not interact with
> >outside world at that time.
> >
> >Whitelist system handlers which run even when VM is stopped.
> >These are specific handlers like monitor, gdbstub, migration.
> >I'm not really sure about ui: spice and vnc: do they need to run?
> 
> Yes, vnc and spice should run.  They serve the clients.  They also
> don't change device state.
> 
> cheers,
>   Gerd
> 
> PS: Well, in the spice case this isn't 100% true, the handlers can
> change qxl device state in some cases.  But spice has a start/stop
> handler which takes care to flush all outstanding work which could
> change device state on vmstop, so it is save to keep the handlers
> active when the vm is in stopped state.

So here's how I see it so far: assuming we want to avoid changing vm
state on vmstop

	- flush bdrv and aio on vmstop
		(this is so we don't need to stop them)
	- keep running aio
	- stop running bh
	- stop running slirp
	- keep running host/rt timers
	- keep running fd callbacks for migration
	- keep running fd callbacks for ui: vnc, spice

Note: only clock=vm can satisfy this requirement.
Default clock=rtc keeps running when VM is stopped,
this is by design.
Juan Quintela Nov. 22, 2010, 1:56 p.m. UTC | #3
"Michael S. Tsirkin" <mst@redhat.com> wrote:
> Stop running devices on vmstop, so that VM does not interact with
> outside world at that time.
>
> Whitelist system handlers which run even when VM is stopped.
> These are specific handlers like monitor, gdbstub, migration.
> I'm not really sure about ui: spice and vnc: do they need to run?

>  
> -int qemu_aio_set_fd_handler(int fd,
> +static int qemu_aio_assign_fd_handler(int fd,
>                              IOHandler *io_read,
>                              IOHandler *io_write,
>                              AioFlushHandler *io_flush,
>                              AioProcessQueue *io_process_queue,
> -                            void *opaque)
> +                            void *opaque, bool system)
>  {

New names much better O:-)

> -        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
> +        qemu_set_systemk_fd_handler(vs->csock, NULL, vnc_client_read, NULL, vs);

typo?  it would not compile (extra k on syntemk)


> +static inline void call_ioh_fds(IOHandlerRecordList *list,
> +                                  fd_set *rfds, fd_set *wfds)
> +{
> +    IOHandlerRecord *ioh, *pioh;
> +
> +    QLIST_FOREACH_SAFE(ioh, list, next, pioh) {
> +        if (ioh->deleted) {
> +            QLIST_REMOVE(ioh, next);
> +            qemu_free(ioh);
> +            continue;
> +        }
> +        if (ioh->fd_read && FD_ISSET(ioh->fd, rfds)) {
> +            ioh->fd_read(ioh->opaque);
> +            if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
> +                FD_CLR(ioh->fd, rfds);
> +        }
> +        if (ioh->fd_write && FD_ISSET(ioh->fd, wfds)) {
> +            ioh->fd_write(ioh->opaque);
> +        }

Notice this loop has been changed to be able that the fd_read to delete
the handle.

> -    }
>  
> -    slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
> +        slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));

white space damage?

I haven't tested it either, to know if changing things to the bottom
handler would improve things.

Later, Juan.
diff mbox

Patch

diff --git a/aio.c b/aio.c
index 2f08655..0d50c87 100644
--- a/aio.c
+++ b/aio.c
@@ -52,14 +52,15 @@  static AioHandler *find_aio_handler(int fd)
     return NULL;
 }
 
-int qemu_aio_set_fd_handler(int fd,
+static int qemu_aio_assign_fd_handler(int fd,
                             IOHandler *io_read,
                             IOHandler *io_write,
                             AioFlushHandler *io_flush,
                             AioProcessQueue *io_process_queue,
-                            void *opaque)
+                            void *opaque, bool system)
 {
     AioHandler *node;
+    int r;
 
     node = find_aio_handler(fd);
 
@@ -93,11 +94,34 @@  int qemu_aio_set_fd_handler(int fd,
         node->opaque = opaque;
     }
 
-    qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
-
+    r = system ? qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque) :
+        qemu_set_system_fd_handler(fd, NULL, io_read, io_write, opaque);
+    assert(!r);
     return 0;
 }
 
+int qemu_aio_set_fd_handler(int fd,
+                            IOHandler *io_read,
+                            IOHandler *io_write,
+                            AioFlushHandler *io_flush,
+                            AioProcessQueue *io_process_queue,
+                            void *opaque)
+{
+    return qemu_aio_assign_fd_handler(fd, io_read, io_write, io_flush,
+                                      io_process_queue, opaque, false);
+}
+
+int qemu_aio_set_system_fd_handler(int fd,
+                            IOHandler *io_read,
+                            IOHandler *io_write,
+                            AioFlushHandler *io_flush,
+                            AioProcessQueue *io_process_queue,
+                            void *opaque)
+{
+    return qemu_aio_assign_fd_handler(fd, io_read, io_write, io_flush,
+                                      io_process_queue, opaque, true);
+}
+
 void qemu_aio_flush(void)
 {
     AioHandler *node;
diff --git a/cmd.c b/cmd.c
index db2c9c4..26dcfe4 100644
--- a/cmd.c
+++ b/cmd.c
@@ -154,7 +154,7 @@  static void prep_fetchline(void *opaque)
 {
     int *fetchable = opaque;
 
-    qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
+    qemu_aio_set_system_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
     *fetchable= 1;
 }
 
@@ -202,8 +202,8 @@  command_loop(void)
         if (!prompted) {
             printf("%s", get_prompt());
             fflush(stdout);
-            qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL,
-                                    NULL, &fetchable);
+            qemu_aio_set_system_fd_handler(STDIN_FILENO, prep_fetchline,
+                                           NULL, NULL, NULL, &fetchable);
             prompted = 1;
         }
 
@@ -228,7 +228,7 @@  command_loop(void)
         prompted = 0;
         fetchable = 0;
 	}
-    qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
+    qemu_aio_set_system_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
 }
 
 /* from libxcmd/input.c */
diff --git a/gdbstub.c b/gdbstub.c
index eb8465d..1dd53d9 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -2653,7 +2653,7 @@  int gdbserver_start(const char *device)
             sigaction(SIGINT, &act, NULL);
         }
 #endif
-        chr = qemu_chr_open("gdb", device, NULL);
+        chr = qemu_chr_open_system("gdb", device, NULL);
         if (!chr)
             return -1;
 
diff --git a/migration-exec.c b/migration-exec.c
index 14718dd..a8026bf 100644
--- a/migration-exec.c
+++ b/migration-exec.c
@@ -123,7 +123,7 @@  static void exec_accept_incoming_migration(void *opaque)
     QEMUFile *f = opaque;
 
     process_incoming_migration(f);
-    qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
     qemu_fclose(f);
 }
 
@@ -138,7 +138,7 @@  int exec_start_incoming_migration(const char *command)
         return -errno;
     }
 
-    qemu_set_fd_handler2(qemu_stdio_fd(f), NULL,
+    qemu_set_system_fd_handler(qemu_stdio_fd(f), NULL,
 			 exec_accept_incoming_migration, NULL, f);
 
     return 0;
diff --git a/migration-fd.c b/migration-fd.c
index 6d14505..e7f8c1f 100644
--- a/migration-fd.c
+++ b/migration-fd.c
@@ -106,7 +106,7 @@  static void fd_accept_incoming_migration(void *opaque)
     QEMUFile *f = opaque;
 
     process_incoming_migration(f);
-    qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
     qemu_fclose(f);
 }
 
@@ -124,7 +124,7 @@  int fd_start_incoming_migration(const char *infd)
         return -errno;
     }
 
-    qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f);
+    qemu_set_system_fd_handler(fd, NULL, fd_accept_incoming_migration, NULL, f);
 
     return 0;
 }
diff --git a/migration-tcp.c b/migration-tcp.c
index b55f419..7ea0f5b 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -66,7 +66,7 @@  static void tcp_wait_for_connect(void *opaque)
         return;
     }
 
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s->fd, NULL, NULL, NULL, NULL);
 
     if (val == 0)
         migrate_fd_connect(s);
@@ -123,7 +123,7 @@  MigrationState *tcp_start_outgoing_migration(Monitor *mon,
             ret = -(s->get_error(s));
 
         if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
-            qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
+            qemu_set_system_fd_handler(s->fd, NULL, NULL, tcp_wait_for_connect, s);
     } while (ret == -EINTR);
 
     if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
@@ -165,7 +165,7 @@  static void tcp_accept_incoming_migration(void *opaque)
 out:
     close(c);
 out2:
-    qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s, NULL, NULL, NULL, NULL);
     close(s);
 }
 
@@ -193,7 +193,7 @@  int tcp_start_incoming_migration(const char *host_port)
     if (listen(s, 1) == -1)
         goto err;
 
-    qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
+    qemu_set_system_fd_handler(s, NULL, tcp_accept_incoming_migration, NULL,
                          (void *)(unsigned long)s);
 
     return 0;
diff --git a/migration-unix.c b/migration-unix.c
index 57232c0..e32fe6b 100644
--- a/migration-unix.c
+++ b/migration-unix.c
@@ -65,7 +65,7 @@  static void unix_wait_for_connect(void *opaque)
         return;
     }
 
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s->fd, NULL, NULL, NULL, NULL);
 
     if (val == 0)
         migrate_fd_connect(s);
@@ -118,7 +118,7 @@  MigrationState *unix_start_outgoing_migration(Monitor *mon,
 	    ret = -(s->get_error(s));
 
         if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
-	    qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s);
+	    qemu_set_system_fd_handler(s->fd, NULL, NULL, unix_wait_for_connect, s);
     } while (ret == -EINTR);
 
     if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
@@ -171,7 +171,7 @@  static void unix_accept_incoming_migration(void *opaque)
     process_incoming_migration(f);
     qemu_fclose(f);
 out:
-    qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s, NULL, NULL, NULL, NULL);
     close(s);
     close(c);
 }
@@ -203,7 +203,7 @@  int unix_start_incoming_migration(const char *path)
         goto err;
     }
 
-    qemu_set_fd_handler2(sock, NULL, unix_accept_incoming_migration, NULL,
+    qemu_set_system_fd_handler(sock, NULL, unix_accept_incoming_migration, NULL,
 			 (void *)(unsigned long)sock);
 
     return 0;
diff --git a/migration.c b/migration.c
index 9ee8b17..7597069 100644
--- a/migration.c
+++ b/migration.c
@@ -273,7 +273,7 @@  int migrate_fd_cleanup(FdMigrationState *s)
 {
     int ret = 0;
 
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s->fd, NULL, NULL, NULL, NULL);
 
     if (s->file) {
         DPRINTF("closing file\n");
@@ -300,7 +300,7 @@  void migrate_fd_put_notify(void *opaque)
 {
     FdMigrationState *s = opaque;
 
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s->fd, NULL, NULL, NULL, NULL);
     qemu_file_put_notify(s->file);
 }
 
@@ -317,7 +317,7 @@  ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
         ret = -(s->get_error(s));
 
     if (ret == -EAGAIN) {
-        qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s);
+        qemu_set_system_fd_handler(s->fd, NULL, NULL, migrate_fd_put_notify, s);
     } else if (ret < 0) {
         if (s->mon) {
             monitor_resume(s->mon);
@@ -445,6 +445,6 @@  int migrate_fd_close(void *opaque)
 {
     FdMigrationState *s = opaque;
 
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(s->fd, NULL, NULL, NULL, NULL);
     return s->close(s);
 }
diff --git a/qemu-aio.h b/qemu-aio.h
index 3bdd749..4223d34 100644
--- a/qemu-aio.h
+++ b/qemu-aio.h
@@ -56,4 +56,11 @@  int qemu_aio_set_fd_handler(int fd,
                             AioProcessQueue *io_process_queue,
                             void *opaque);
 
+/* Same but for system handlers, which run when VM is stopped. */
+int qemu_aio_set_system_fd_handler(int fd,
+                            IOHandler *io_read,
+                            IOHandler *io_write,
+                            AioFlushHandler *io_flush,
+                            AioProcessQueue *io_process_queue,
+                            void *opaque);
 #endif
diff --git a/qemu-char.c b/qemu-char.c
index 88997f9..6a26787 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2500,6 +2500,16 @@  CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*i
     return chr;
 }
 
+CharDriverState *qemu_chr_system_open(const char *label,
+                                      const char *filename, void (*init)(struct CharDriverState *s))
+{
+    CharDriverState *chr = qemu_chr_open(label, filename, init);
+    if (chr) {
+        chr->system = 1;
+    }
+    return chr;
+}
+
 void qemu_chr_close(CharDriverState *chr)
 {
     QTAILQ_REMOVE(&chardevs, chr, next);
diff --git a/qemu-char.h b/qemu-char.h
index 18ad12b..2f35fc9 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -68,6 +68,7 @@  struct CharDriverState {
     char *label;
     char *filename;
     int opened;
+    int system;
     QTAILQ_ENTRY(CharDriverState) next;
 };
 
@@ -75,6 +76,8 @@  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
                                     void (*init)(struct CharDriverState *s));
 CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s));
+CharDriverState *qemu_chr_system_open(const char *label, const char *filename,
+				      void (*init)(struct CharDriverState *s));
 void qemu_chr_close(CharDriverState *chr);
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
     GCC_FMT_ATTR(2, 3);
@@ -111,4 +114,9 @@  int qemu_set_fd_handler(int fd,
                         IOHandler *fd_read,
                         IOHandler *fd_write,
                         void *opaque);
+int qemu_set_system_fd_handler(int fd,
+                         IOCanReadHandler *fd_read_poll,
+                         IOHandler *fd_read,
+                         IOHandler *fd_write,
+                         void *opaque);
 #endif
diff --git a/qemu-kvm.c b/qemu-kvm.c
index 471306b..c99b597 100644
--- a/qemu-kvm.c
+++ b/qemu-kvm.c
@@ -1577,7 +1577,7 @@  int kvm_main_loop(void)
 
     fcntl(sigfd, F_SETFL, O_NONBLOCK);
 
-    qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+    qemu_set_system_fd_handler(sigfd, NULL, sigfd_handler, NULL,
                          (void *)(unsigned long) sigfd);
 
     pthread_cond_broadcast(&qemu_system_cond);
diff --git a/qemu-tool.c b/qemu-tool.c
index 392e1c9..0255ff3 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -111,3 +111,12 @@  int qemu_set_fd_handler2(int fd,
 {
     return 0;
 }
+
+int qemu_set_system_fd_handler(int fd,
+                               IOCanReadHandler *fd_read_poll,
+                               IOHandler *fd_read,
+                               IOHandler *fd_write,
+                               void *opaque)
+{
+    return 0;
+}
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 6c404b3..fdcb303 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -97,7 +97,7 @@  static void watch_update_mask(SpiceWatch *watch, int event_mask)
     if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
         on_read = watch_write;
     }
-    qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
+    qemu_set_system_fd_handler(watch->fd, NULL, on_read, on_write, watch);
 }
 
 static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index a51ddc8..58f199b 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -84,7 +84,7 @@  long vnc_client_write_sasl(VncState *vs)
      * SASL encoded output
      */
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_systemk_fd_handler(vs->csock, NULL, vnc_client_read, NULL, vs);
     }
 
     return ret;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 07c1691..489fc11 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -71,9 +71,9 @@  static int vnc_start_vencrypt_handshake(struct VncState *vs) {
        if (!gnutls_error_is_fatal(ret)) {
            VNC_DEBUG("Handshake interrupted (blocking)\n");
            if (!gnutls_record_get_direction(vs->tls.session))
-               qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
+               qemu_set_system_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, NULL, vs);
            else
-               qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
+               qemu_set_system_fd_handler(vs->csock, NULL, NULL, vnc_tls_handshake_io, vs);
            return 0;
        }
        VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
@@ -93,7 +93,7 @@  static int vnc_start_vencrypt_handshake(struct VncState *vs) {
 
     VNC_DEBUG("Handshake done, switching to TLS data mode\n");
     vs->tls.wiremode = VNC_WIREMODE_TLS;
-    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+    qemu_set_system_fd_handler(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
 
     start_auth_vencrypt_subauth(vs);
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 864342e..b7bdddf 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1005,7 +1005,7 @@  static void vnc_disconnect_start(VncState *vs)
 {
     if (vs->csock == -1)
         return;
-    qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+    qemu_set_system_fd_handler(vs->csock, NULL, NULL, NULL, NULL);
     closesocket(vs->csock);
     vs->csock = -1;
 }
@@ -1155,7 +1155,7 @@  static long vnc_client_write_plain(VncState *vs)
     vs->output.offset -= ret;
 
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_system_fd_handler(vs->csock, NULL, vnc_client_read, NULL, vs);
     }
 
     return ret;
@@ -1189,7 +1189,7 @@  void vnc_client_write(void *opaque)
     if (vs->output.offset) {
         vnc_client_write_locked(opaque);
     } else if (vs->csock != -1) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        qemu_set_system_fd_handler(vs->csock, NULL, vnc_client_read, NULL, vs);
     }
     vnc_unlock_output(vs);
 }
@@ -1305,7 +1305,7 @@  void vnc_write(VncState *vs, const void *data, size_t len)
     buffer_reserve(&vs->output, len);
 
     if (vs->csock != -1 && buffer_empty(&vs->output)) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+        qemu_set_system_fd_handler(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
     }
 
     buffer_append(&vs->output, data, len);
@@ -2369,7 +2369,7 @@  static void vnc_connect(VncDisplay *vd, int csock)
     VNC_DEBUG("New client on socket %d\n", csock);
     dcl->idle = 0;
     socket_set_nonblock(vs->csock);
-    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+    qemu_set_system_fd_handler(vs->csock, NULL, vnc_client_read, NULL, vs);
 
     vnc_client_cache_addr(vs);
     vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
@@ -2471,7 +2471,7 @@  void vnc_display_close(DisplayState *ds)
         vs->display = NULL;
     }
     if (vs->lsock != -1) {
-        qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+        qemu_set_system_fd_handler(vs->lsock, NULL, NULL, NULL, NULL);
         close(vs->lsock);
         vs->lsock = -1;
     }
@@ -2736,5 +2736,5 @@  int vnc_display_open(DisplayState *ds, const char *display)
             vs->display = dpy;
         }
     }
-    return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
+    return qemu_set_system_fd_handler(vs->lsock, NULL, vnc_listen_read, NULL, vs);
 }
diff --git a/vl.c b/vl.c
index 37ed560..1d50e00 100644
--- a/vl.c
+++ b/vl.c
@@ -929,34 +929,36 @@  typedef struct IOHandlerRecord {
     QLIST_ENTRY(IOHandlerRecord) next;
 } IOHandlerRecord;
 
-static QLIST_HEAD(, IOHandlerRecord) io_handlers =
-    QLIST_HEAD_INITIALIZER(io_handlers);
+typedef QLIST_HEAD(, IOHandlerRecord) IOHandlerRecordList;
+static IOHandlerRecordList device_io_handlers =
+    QLIST_HEAD_INITIALIZER(device_io_handlers);
 
+static IOHandlerRecordList system_io_handlers =
+    QLIST_HEAD_INITIALIZER(system_io_handlers);
 
-/* XXX: fd_read_poll should be suppressed, but an API change is
-   necessary in the character devices to suppress fd_can_read(). */
-int qemu_set_fd_handler2(int fd,
-                         IOCanReadHandler *fd_read_poll,
-                         IOHandler *fd_read,
-                         IOHandler *fd_write,
-                         void *opaque)
+static int qemu_assign_fd_handler(int fd,
+                                  IOCanReadHandler *fd_read_poll,
+                                  IOHandler *fd_read,
+                                  IOHandler *fd_write,
+                                  void *opaque,
+                                  IOHandlerRecordList *list)
 {
     IOHandlerRecord *ioh;
 
     if (!fd_read && !fd_write) {
-        QLIST_FOREACH(ioh, &io_handlers, next) {
+        QLIST_FOREACH(ioh, list, next) {
             if (ioh->fd == fd) {
                 ioh->deleted = 1;
                 break;
             }
         }
     } else {
-        QLIST_FOREACH(ioh, &io_handlers, next) {
+        QLIST_FOREACH(ioh, list, next) {
             if (ioh->fd == fd)
                 goto found;
         }
         ioh = qemu_mallocz(sizeof(IOHandlerRecord));
-        QLIST_INSERT_HEAD(&io_handlers, ioh, next);
+        QLIST_INSERT_HEAD(list, ioh, next);
     found:
         ioh->fd = fd;
         ioh->fd_read_poll = fd_read_poll;
@@ -969,6 +971,19 @@  int qemu_set_fd_handler2(int fd,
     return 0;
 }
 
+
+/* XXX: fd_read_poll should be suppressed, but an API change is
+   necessary in the character devices to suppress fd_can_read(). */
+int qemu_set_fd_handler2(int fd,
+                         IOCanReadHandler *fd_read_poll,
+                         IOHandler *fd_read,
+                         IOHandler *fd_write,
+                         void *opaque)
+{
+    return qemu_assign_fd_handler(fd, fd_read_poll, fd_read, fd_write, opaque,
+                                  &device_io_handlers);
+}
+
 int qemu_set_fd_handler(int fd,
                         IOHandler *fd_read,
                         IOHandler *fd_write,
@@ -977,6 +992,18 @@  int qemu_set_fd_handler(int fd,
     return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
 }
 
+int qemu_set_system_fd_handler(int fd,
+                         IOCanReadHandler *fd_read_poll,
+                         IOHandler *fd_read,
+                         IOHandler *fd_write,
+                         void *opaque)
+{
+    qemu_assign_fd_handler(fd, fd_read_poll, fd_read, fd_write, opaque,
+                                  &device_io_handlers);
+    return qemu_assign_fd_handler(fd, fd_read_poll, fd_read, fd_write, opaque,
+                                  &system_io_handlers);
+}
+
 /***********************************************************/
 /* machine registration */
 
@@ -1213,13 +1240,57 @@  void qemu_system_powerdown_request(void)
     qemu_notify_event();
 }
 
-void main_loop_wait(int nonblocking)
+static inline int get_ioh_fds(IOHandlerRecordList *list,
+                              int nfds, fd_set *rfds, fd_set *wfds)
 {
     IOHandlerRecord *ioh;
+    QLIST_FOREACH(ioh, list, next) {
+        if (ioh->deleted)
+            continue;
+        if (ioh->fd_read &&
+            (!ioh->fd_read_poll ||
+             ioh->fd_read_poll(ioh->opaque) != 0)) {
+            FD_SET(ioh->fd, rfds);
+            if (ioh->fd > nfds)
+                nfds = ioh->fd;
+        }
+        if (ioh->fd_write) {
+            FD_SET(ioh->fd, wfds);
+            if (ioh->fd > nfds)
+                nfds = ioh->fd;
+        }
+    }
+    return nfds;
+}
+
+static inline void call_ioh_fds(IOHandlerRecordList *list,
+                                  fd_set *rfds, fd_set *wfds)
+{
+    IOHandlerRecord *ioh, *pioh;
+
+    QLIST_FOREACH_SAFE(ioh, list, next, pioh) {
+        if (ioh->deleted) {
+            QLIST_REMOVE(ioh, next);
+            qemu_free(ioh);
+            continue;
+        }
+        if (ioh->fd_read && FD_ISSET(ioh->fd, rfds)) {
+            ioh->fd_read(ioh->opaque);
+            if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
+                FD_CLR(ioh->fd, rfds);
+        }
+        if (ioh->fd_write && FD_ISSET(ioh->fd, wfds)) {
+            ioh->fd_write(ioh->opaque);
+        }
+    }
+}
+void main_loop_wait(int nonblocking)
+{
     fd_set rfds, wfds, xfds;
     int ret, nfds;
     struct timeval tv;
     int timeout;
+    IOHandlerRecordList *handlers;
 
     if (nonblocking)
         timeout = 0;
@@ -1231,64 +1302,47 @@  void main_loop_wait(int nonblocking)
     os_host_main_loop_wait(&timeout);
 
     /* poll any events */
-    /* XXX: separate device handlers from system ones */
     nfds = -1;
     FD_ZERO(&rfds);
     FD_ZERO(&wfds);
     FD_ZERO(&xfds);
-    QLIST_FOREACH(ioh, &io_handlers, next) {
-        if (ioh->deleted)
-            continue;
-        if (ioh->fd_read &&
-            (!ioh->fd_read_poll ||
-             ioh->fd_read_poll(ioh->opaque) != 0)) {
-            FD_SET(ioh->fd, &rfds);
-            if (ioh->fd > nfds)
-                nfds = ioh->fd;
-        }
-        if (ioh->fd_write) {
-            FD_SET(ioh->fd, &wfds);
-            if (ioh->fd > nfds)
-                nfds = ioh->fd;
-        }
-    }
 
-    tv.tv_sec = timeout / 1000;
-    tv.tv_usec = (timeout % 1000) * 1000;
+    if (vm_running) {
 
-    slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+        nfds = get_ioh_fds(&device_io_handlers, nfds, &rfds, &wfds);
 
-    qemu_mutex_unlock_iothread();
-    ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
-    qemu_mutex_lock_iothread();
-    if (ret > 0) {
-        IOHandlerRecord *pioh;
+        tv.tv_sec = timeout / 1000;
+        tv.tv_usec = (timeout % 1000) * 1000;
 
-        QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
-            if (ioh->deleted) {
-                QLIST_REMOVE(ioh, next);
-                qemu_free(ioh);
-                continue;
-            }
-            if (ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
-                ioh->fd_read(ioh->opaque);
-                if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
-                    FD_CLR(ioh->fd, &rfds);
-            }
-            if (ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
-                ioh->fd_write(ioh->opaque);
-            }
+        slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+
+        qemu_mutex_unlock_iothread();
+        ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+        qemu_mutex_lock_iothread();
+        if (ret > 0) {
+            call_ioh_fds(&device_io_handlers, &rfds, &wfds);
         }
-    }
 
-    slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
+        slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
 
-    qemu_run_all_timers();
+        qemu_run_all_timers();
 
-    /* Check bottom-halves last in case any of the earlier events triggered
-       them.  */
-    qemu_bh_poll();
+        /* Check bottom-halves last in case any of the earlier events triggered
+           them.  */
+        qemu_bh_poll();
+    } else {
+        nfds = get_ioh_fds(&system_io_handlers, nfds, &rfds, &wfds);
+
+        tv.tv_sec = timeout / 1000;
+        tv.tv_usec = (timeout % 1000) * 1000;
 
+        qemu_mutex_unlock_iothread();
+        ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+        qemu_mutex_lock_iothread();
+        if (ret > 0) {
+            call_ioh_fds(&system_io_handlers, &rfds, &wfds);
+        }
+    }
 }
 
 static int vm_can_run(void)