Blame 0002-qxl-implement-get_command-in-vga-mode-without-locks.patch

Justin M. Forbes da9298
>From 97e291086fef45762e0278e85ab1d231a9902bbb Mon Sep 17 00:00:00 2001
Justin M. Forbes da9298
From: Uri Lublin <uril@redhat.com>
Justin M. Forbes da9298
Date: Wed, 16 Mar 2011 15:43:45 +0100
Justin M. Forbes da9298
Subject: [PATCH 2/4] qxl: implement get_command in vga mode without locks
Justin M. Forbes da9298
Justin M. Forbes da9298
This patch and the next drop the requirement to lose the global qemu
Justin M. Forbes da9298
mutex during dispatcher calls. This patch enables it, the next drops
Justin M. Forbes da9298
the unlock/lock pairs around dispatcher calls.
Justin M. Forbes da9298
Justin M. Forbes da9298
The current solution of dropping the locks is buggy:
Justin M. Forbes da9298
 * it allows multiple dispatcher calls from two vcpu threads, the
Justin M. Forbes da9298
 dispatcher doesn't handle that by design (single fd, not locked, can't
Justin M. Forbes da9298
 handle writes from two threads)
Justin M. Forbes da9298
 * it requires us to keep track of cpu_single_env, which is magic.
Justin M. Forbes da9298
Justin M. Forbes da9298
The solution implemented in this patch and the next (the next just
Justin M. Forbes da9298
drops the locks, this patch allows that to work):
Justin M. Forbes da9298
 * the only operation that needed locking was qemu_create_simple_update,
Justin M. Forbes da9298
 * it required locking because it was called from the spice-server thread.
Justin M. Forbes da9298
 * do it in the iothread by reusing the existing pipe used for set_irq.
Justin M. Forbes da9298
Justin M. Forbes da9298
The current flow implemented is now:
Justin M. Forbes da9298
spice-server thread:
Justin M. Forbes da9298
 qxl.c:interface_get_command (called either by polling or from wakeup)
Justin M. Forbes da9298
  if update!=NULL:
Justin M. Forbes da9298
   waiting_for_update=0
Justin M. Forbes da9298
   update=NULL
Justin M. Forbes da9298
   return update
Justin M. Forbes da9298
  else:
Justin M. Forbes da9298
   if not waiting_for_update:
Justin M. Forbes da9298
    waiting_for_update=1
Justin M. Forbes da9298
    write to pipe, which is read by iothread (main thread)
Justin M. Forbes da9298
Justin M. Forbes da9298
iothread:
Justin M. Forbes da9298
 wakeup from select,
Justin M. Forbes da9298
 qxl.c:pipe_read
Justin M. Forbes da9298
  update=qemu_create_simple_update()
Justin M. Forbes da9298
  wakeup spice-server thread by calling d.worker->wakeup(d.worker)
Justin M. Forbes da9298
---
Justin M. Forbes da9298
 hw/qxl.c           |   81 +++++++++++++++++++++++++++++++++++++++------------
Justin M. Forbes da9298
 ui/spice-display.c |   75 +++++++++++++++++++++++++++++++++++++++++++----
Justin M. Forbes da9298
 ui/spice-display.h |   16 ++++++++++
Justin M. Forbes da9298
 3 files changed, 146 insertions(+), 26 deletions(-)
Justin M. Forbes da9298
Justin M. Forbes da9298
diff --git a/hw/qxl.c b/hw/qxl.c
Justin M. Forbes da9298
index 201698f..64580f1 100644
Justin M. Forbes da9298
--- a/hw/qxl.c
Justin M. Forbes da9298
+++ b/hw/qxl.c
Justin M. Forbes da9298
@@ -340,7 +340,6 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
Justin M. Forbes da9298
 static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
Justin M. Forbes da9298
 {
Justin M. Forbes da9298
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
Justin M. Forbes da9298
-    SimpleSpiceUpdate *update;
Justin M. Forbes da9298
     QXLCommandRing *ring;
Justin M. Forbes da9298
     QXLCommand *cmd;
Justin M. Forbes da9298
     int notify;
Justin M. Forbes da9298
@@ -348,16 +347,25 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
Justin M. Forbes da9298
     switch (qxl->mode) {
Justin M. Forbes da9298
     case QXL_MODE_VGA:
Justin M. Forbes da9298
         dprint(qxl, 2, "%s: vga\n", __FUNCTION__);
Justin M. Forbes da9298
-        update = qemu_spice_create_update(&qxl->ssd);
Justin M. Forbes da9298
-        if (update == NULL) {
Justin M. Forbes da9298
-            return false;
Justin M. Forbes da9298
+        if (qxl_vga_mode_get_command(&qxl->ssd, ext)) {
Justin M. Forbes da9298
+            qxl_log_command(qxl, "vga", ext);
Justin M. Forbes da9298
+            return true;
Justin M. Forbes da9298
         }
Justin M. Forbes da9298
-        *ext = update->ext;
Justin M. Forbes da9298
-        qxl_log_command(qxl, "vga", ext);
Justin M. Forbes da9298
-        return true;
Justin M. Forbes da9298
+        return false;
Justin M. Forbes da9298
     case QXL_MODE_COMPAT:
Justin M. Forbes da9298
     case QXL_MODE_NATIVE:
Justin M. Forbes da9298
     case QXL_MODE_UNDEFINED:
Justin M. Forbes da9298
+        /* flush any existing updates that we didn't send to the guest.
Justin M. Forbes da9298
+         * since update != NULL it means the server didn't get it, and
Justin M. Forbes da9298
+         * because we changed mode to != QXL_MODE_VGA, it won't. */
Justin M. Forbes da9298
+        if (qxl->ssd.update != NULL) {
Justin M. Forbes da9298
+            if (qxl->ssd.update != QXL_EMPTY_UPDATE) {
Justin M. Forbes da9298
+                qemu_spice_destroy_update(&qxl->ssd, qxl->ssd.update);
Justin M. Forbes da9298
+            }
Justin M. Forbes da9298
+            qxl->ssd.update = NULL;
Justin M. Forbes da9298
+            qxl->ssd.waiting_for_update = 0;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+        /* */
Justin M. Forbes da9298
         dprint(qxl, 2, "%s: %s\n", __FUNCTION__,
Justin M. Forbes da9298
                qxl->cmdflags ? "compat" : "native");
Justin M. Forbes da9298
         ring = &qxl->ram->cmd_ring;
Justin M. Forbes da9298
@@ -1057,17 +1065,50 @@ static void qxl_map(PCIDevice *pci, int region_num,
Justin M. Forbes da9298
 
Justin M. Forbes da9298
 static void pipe_read(void *opaque)
Justin M. Forbes da9298
 {
Justin M. Forbes da9298
-    PCIQXLDevice *d = opaque;
Justin M. Forbes da9298
-    char dummy;
Justin M. Forbes da9298
-    int len;
Justin M. Forbes da9298
-
Justin M. Forbes da9298
-    do {
Justin M. Forbes da9298
-        len = read(d->ssd.pipe[0], &dummy, sizeof(dummy));
Justin M. Forbes da9298
-    } while (len == sizeof(dummy));
Justin M. Forbes da9298
-    qxl_set_irq(d);
Justin M. Forbes da9298
+    SimpleSpiceDisplay *ssd = opaque;
Justin M. Forbes da9298
+    unsigned char cmd;
Justin M. Forbes da9298
+    int len, set_irq = 0;
Justin M. Forbes da9298
+    int create_update = 0;
Justin M. Forbes da9298
+
Justin M. Forbes da9298
+    while (1) {
Justin M. Forbes da9298
+        cmd = 0;
Justin M. Forbes da9298
+        len = read(ssd->pipe[0], &cmd, sizeof(cmd));
Justin M. Forbes da9298
+        if (len < 0) {
Justin M. Forbes da9298
+            if (errno == EINTR) {
Justin M. Forbes da9298
+                continue;
Justin M. Forbes da9298
+            }
Justin M. Forbes da9298
+            if (errno == EAGAIN) {
Justin M. Forbes da9298
+                break;
Justin M. Forbes da9298
+            }
Justin M. Forbes da9298
+            perror("qxl: pipe_read: read failed");
Justin M. Forbes da9298
+            break;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+        switch (cmd) {
Justin M. Forbes da9298
+        case QXL_SERVER_SET_IRQ:
Justin M. Forbes da9298
+            set_irq = 1;
Justin M. Forbes da9298
+            break;
Justin M. Forbes da9298
+        case QXL_SERVER_CREATE_UPDATE:
Justin M. Forbes da9298
+            create_update = 1;
Justin M. Forbes da9298
+            break;
Justin M. Forbes da9298
+        default:
Justin M. Forbes da9298
+            fprintf(stderr, "%s: unknown cmd %u\n", __FUNCTION__, cmd);
Justin M. Forbes da9298
+            abort();
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
+    /* no need to do either operation more than once */
Justin M. Forbes da9298
+    if (create_update) {
Justin M. Forbes da9298
+        assert(ssd->update == NULL);
Justin M. Forbes da9298
+        ssd->update = qemu_spice_create_update(ssd);
Justin M. Forbes da9298
+        if (ssd->update == NULL) {
Justin M. Forbes da9298
+            ssd->update = QXL_EMPTY_UPDATE;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+        ssd->worker->wakeup(ssd->worker);
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
+    if (set_irq) {
Justin M. Forbes da9298
+        qxl_set_irq(container_of(ssd, PCIQXLDevice, ssd));
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
 }
Justin M. Forbes da9298
 
Justin M. Forbes da9298
-/* called from spice server thread context only */
Justin M. Forbes da9298
 static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
Justin M. Forbes da9298
 {
Justin M. Forbes da9298
     uint32_t old_pending;
Justin M. Forbes da9298
@@ -1082,9 +1123,11 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
Justin M. Forbes da9298
         /* running in io_thread thread */
Justin M. Forbes da9298
         qxl_set_irq(d);
Justin M. Forbes da9298
     } else {
Justin M. Forbes da9298
-        if (write(d->ssd.pipe[1], d, 1) != 1) {
Justin M. Forbes da9298
-            dprint(d, 1, "%s: write to pipe failed\n", __FUNCTION__);
Justin M. Forbes da9298
-        }
Justin M. Forbes da9298
+        /* called from spice-server thread */
Justin M. Forbes da9298
+        int ret;
Justin M. Forbes da9298
+        unsigned char ack = QXL_SERVER_SET_IRQ;
Justin M. Forbes da9298
+        ret = write(d->ssd.pipe[1], &ack, 1);
Justin M. Forbes da9298
+        assert(ret == 1);
Justin M. Forbes da9298
     }
Justin M. Forbes da9298
 }
Justin M. Forbes da9298
 
Justin M. Forbes da9298
diff --git a/ui/spice-display.c b/ui/spice-display.c
Justin M. Forbes da9298
index ec6e0cb..bdd14b8 100644
Justin M. Forbes da9298
--- a/ui/spice-display.c
Justin M. Forbes da9298
+++ b/ui/spice-display.c
Justin M. Forbes da9298
@@ -294,18 +294,39 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
Justin M. Forbes da9298
     info->n_surfaces = NUM_SURFACES;
Justin M. Forbes da9298
 }
Justin M. Forbes da9298
 
Justin M. Forbes da9298
+/* Called from spice server thread context (via interface_get_command) */
Justin M. Forbes da9298
+int qxl_vga_mode_get_command(
Justin M. Forbes da9298
+    SimpleSpiceDisplay *ssd, struct QXLCommandExt *ext)
Justin M. Forbes da9298
+{
Justin M. Forbes da9298
+    SimpleSpiceUpdate *update;
Justin M. Forbes da9298
+    unsigned char req;
Justin M. Forbes da9298
+    int r;
Justin M. Forbes da9298
+
Justin M. Forbes da9298
+    update = ssd->update;
Justin M. Forbes da9298
+    if (update != NULL) {
Justin M. Forbes da9298
+        ssd->waiting_for_update = 0;
Justin M. Forbes da9298
+        ssd->update = NULL;
Justin M. Forbes da9298
+        if (update != QXL_EMPTY_UPDATE) {
Justin M. Forbes da9298
+            *ext = update->ext;
Justin M. Forbes da9298
+            return true;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+    } else {
Justin M. Forbes da9298
+        if (!ssd->waiting_for_update) {
Justin M. Forbes da9298
+            ssd->waiting_for_update = 1;
Justin M. Forbes da9298
+            req = QXL_SERVER_CREATE_UPDATE;
Justin M. Forbes da9298
+            r = write(ssd->pipe[1], &req , 1);
Justin M. Forbes da9298
+            assert(r == 1);
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
+    return false;
Justin M. Forbes da9298
+}
Justin M. Forbes da9298
+
Justin M. Forbes da9298
 static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
Justin M. Forbes da9298
 {
Justin M. Forbes da9298
     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
Justin M. Forbes da9298
-    SimpleSpiceUpdate *update;
Justin M. Forbes da9298
 
Justin M. Forbes da9298
     dprint(3, "%s:\n", __FUNCTION__);
Justin M. Forbes da9298
-    update = qemu_spice_create_update(ssd);
Justin M. Forbes da9298
-    if (update == NULL) {
Justin M. Forbes da9298
-        return false;
Justin M. Forbes da9298
-    }
Justin M. Forbes da9298
-    *ext = update->ext;
Justin M. Forbes da9298
-    return true;
Justin M. Forbes da9298
+    return qxl_vga_mode_get_command(ssd, ext);
Justin M. Forbes da9298
 }
Justin M. Forbes da9298
 
Justin M. Forbes da9298
 static int interface_req_cmd_notification(QXLInstance *sin)
Justin M. Forbes da9298
@@ -394,6 +415,45 @@ static DisplayChangeListener display_listener = {
Justin M. Forbes da9298
     .dpy_refresh = display_refresh,
Justin M. Forbes da9298
 };
Justin M. Forbes da9298
 
Justin M. Forbes da9298
+static void pipe_read(void *opaque)
Justin M. Forbes da9298
+{
Justin M. Forbes da9298
+    SimpleSpiceDisplay *ssd = opaque;
Justin M. Forbes da9298
+    unsigned char cmd;
Justin M. Forbes da9298
+    int len, create_update = 0;
Justin M. Forbes da9298
+
Justin M. Forbes da9298
+    while (1) {
Justin M. Forbes da9298
+        cmd = 0;
Justin M. Forbes da9298
+        len = read(ssd->pipe[0], &cmd, sizeof(cmd));
Justin M. Forbes da9298
+        if (len < 0) {
Justin M. Forbes da9298
+            if (errno == EINTR) {
Justin M. Forbes da9298
+                continue;
Justin M. Forbes da9298
+            }
Justin M. Forbes da9298
+            if (errno == EAGAIN) {
Justin M. Forbes da9298
+                break;
Justin M. Forbes da9298
+            }
Justin M. Forbes da9298
+            perror("qxl: pipe_read: read failed");
Justin M. Forbes da9298
+            break;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+        switch (cmd) {
Justin M. Forbes da9298
+        case QXL_SERVER_CREATE_UPDATE:
Justin M. Forbes da9298
+            create_update = 1;
Justin M. Forbes da9298
+            break;
Justin M. Forbes da9298
+        default:
Justin M. Forbes da9298
+            fprintf(stderr, "%s: unknown cmd %u\n", __FUNCTION__, cmd);
Justin M. Forbes da9298
+            abort();
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
+    /* no need to do this more than once */
Justin M. Forbes da9298
+    if (create_update) {
Justin M. Forbes da9298
+        assert(ssd->update == NULL);
Justin M. Forbes da9298
+        ssd->update = qemu_spice_create_update(ssd);
Justin M. Forbes da9298
+        if (ssd->update == NULL) {
Justin M. Forbes da9298
+            ssd->update = QXL_EMPTY_UPDATE;
Justin M. Forbes da9298
+        }
Justin M. Forbes da9298
+        ssd->worker->wakeup(ssd->worker);
Justin M. Forbes da9298
+    }
Justin M. Forbes da9298
+}
Justin M. Forbes da9298
+
Justin M. Forbes da9298
 void qxl_create_server_to_iothread_pipe(SimpleSpiceDisplay *ssd,
Justin M. Forbes da9298
     IOHandler *pipe_read)
Justin M. Forbes da9298
 {
Justin M. Forbes da9298
@@ -427,6 +487,7 @@ void qemu_spice_display_init(DisplayState *ds)
Justin M. Forbes da9298
     qemu_spice_add_interface(&sdpy.qxl.base);
Justin M. Forbes da9298
     assert(sdpy.worker);
Justin M. Forbes da9298
 
Justin M. Forbes da9298
+    qxl_create_server_to_iothread_pipe(&sdpy, pipe_read);
Justin M. Forbes da9298
     qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
Justin M. Forbes da9298
     qemu_spice_create_host_memslot(&sdpy);
Justin M. Forbes da9298
     qemu_spice_create_host_primary(&sdpy);
Justin M. Forbes da9298
diff --git a/ui/spice-display.h b/ui/spice-display.h
Justin M. Forbes da9298
index 3e6cf7c..2be6a34 100644
Justin M. Forbes da9298
--- a/ui/spice-display.h
Justin M. Forbes da9298
+++ b/ui/spice-display.h
Justin M. Forbes da9298
@@ -31,6 +31,15 @@
Justin M. Forbes da9298
 
Justin M. Forbes da9298
 #define NUM_SURFACES 1024
Justin M. Forbes da9298
 
Justin M. Forbes da9298
+/* For commands/requests from server thread to iothread */
Justin M. Forbes da9298
+#define QXL_EMPTY_UPDATE ((void *)-1)
Justin M. Forbes da9298
+enum {
Justin M. Forbes da9298
+    QXL_SERVER_SET_IRQ = 1,
Justin M. Forbes da9298
+    QXL_SERVER_CREATE_UPDATE,
Justin M. Forbes da9298
+};
Justin M. Forbes da9298
+
Justin M. Forbes da9298
+struct SimpleSpiceUpdate;
Justin M. Forbes da9298
+
Justin M. Forbes da9298
 typedef struct SimpleSpiceDisplay {
Justin M. Forbes da9298
     DisplayState *ds;
Justin M. Forbes da9298
     void *buf;
Justin M. Forbes da9298
@@ -48,6 +57,10 @@ typedef struct SimpleSpiceDisplay {
Justin M. Forbes da9298
      * and in native mode) and without qxl */
Justin M. Forbes da9298
     pthread_t          main;
Justin M. Forbes da9298
     int                pipe[2];     /* to iothread */
Justin M. Forbes da9298
+
Justin M. Forbes da9298
+    /* ssd updates (one request/command at a time) */
Justin M. Forbes da9298
+    struct SimpleSpiceUpdate *update;
Justin M. Forbes da9298
+    int waiting_for_update;
Justin M. Forbes da9298
 } SimpleSpiceDisplay;
Justin M. Forbes da9298
 
Justin M. Forbes da9298
 typedef struct SimpleSpiceUpdate {
Justin M. Forbes da9298
@@ -71,6 +84,9 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
Justin M. Forbes da9298
                                int x, int y, int w, int h);
Justin M. Forbes da9298
 void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
Justin M. Forbes da9298
 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);
Justin M. Forbes da9298
+/* shared with qxl.c in vga mode and ui/spice-display (no qxl mode) */
Justin M. Forbes da9298
+int qxl_vga_mode_get_command(
Justin M. Forbes da9298
+    SimpleSpiceDisplay *ssd, struct QXLCommandExt *ext);
Justin M. Forbes da9298
 /* used by both qxl and spice-display */
Justin M. Forbes da9298
 void qxl_create_server_to_iothread_pipe(SimpleSpiceDisplay *ssd,
Justin M. Forbes da9298
     IOHandler *pipe_read);
Justin M. Forbes da9298
-- 
Justin M. Forbes da9298
1.7.3.2
Justin M. Forbes da9298