[Spice-devel] [PATCH qxl-win v3] asynchronous io port support (introduced in revision 3 of qxl device)

Alon Levy alevy at redhat.com
Thu Jul 7 02:43:08 PDT 2011


Fixes same issue in RHBZ#700134, but for a windows guest. Requires a revision 3
pci device, that will be introduced with qemu patches. If the revision is 2 the
old behavior is maintained, namely using the non asynchronous io ports.

qxl revision 3 (QXL_REVISION_V10) gains support for async io operations for

 NOTIFY_OOM
 UPDATE_AREA
 CREATE_PRIMARY
 DESTROY_PRIMARY
 DESTROY_SURFACE_WAIT (not used currently, just exported)
 DESTROY_SURFACES

use that if the revision is large enough.

Async io ports let qemu return faster to the guest, and issue an interrupt
when the operation started by the io write is complete. This means less
time when the qemu global mutex is held.
---
 display/driver.c     |   33 ++++++++++++++++++----------
 display/qxldd.h      |   57 +++++++++++++++++++++++++++++++++++++++++++------
 display/res.c        |   26 ++++++++++++++--------
 include/qxl_driver.h |    8 +++++++
 miniport/qxl.c       |   34 +++++++++++++++++++++++++++++-
 5 files changed, 128 insertions(+), 30 deletions(-)

diff --git a/display/driver.c b/display/driver.c
index 2c88cc5..497df00 100644
--- a/display/driver.c
+++ b/display/driver.c
@@ -572,19 +572,19 @@ static VOID CreatePrimarySurface(PDev *pdev, UINT32 depth, UINT32 format,
     pdev->primary_surface_create->flags = 0;
     pdev->primary_surface_create->type = QXL_SURF_TYPE_PRIMARY;
 
-    WRITE_PORT_UCHAR(pdev->create_primary_port, 0);
+    async_io(pdev, ASYNCABLE_CREATE_PRIMARY, 0);
 }
 
 static void DestroyPrimarySurface(PDev *pdev)
 {
     HideMouse(pdev);
-    WRITE_PORT_UCHAR(pdev->destroy_primary_port, 0);
+    async_io(pdev, ASYNCABLE_DESTROY_PRIMARY, 0);
 }
 
 static void DestroyAllSurfaces(PDev *pdev)
 {
     HideMouse(pdev);
-    WRITE_PORT_UCHAR(pdev->destroy_all_surfaces_port, 0);
+    async_io(pdev, ASYNCABLE_DESTROY_ALL_SURFACES, 0);
 }
 
 BOOL SetHardwareMode(PDev *pdev)
@@ -642,7 +642,7 @@ static BOOLEAN CreateVRamSlot(PDev *pdev)
     *pdev->ram_slot_start = pdev->fb_phys;
     *pdev->ram_slot_end = pdev->fb_phys + pdev->fb_size;
 
-    WRITE_PORT_UCHAR(pdev->memslot_add_port, slot_id);
+    async_io(pdev, ASYNCABLE_MEMSLOT_ADD, slot_id);
 
     pdev->vram_mem_slot = slot_id;
 
@@ -695,10 +695,26 @@ static BOOL PrepareHardware(PDev *pdev)
     pdev->release_ring = dev_info.release_ring;
     pdev->notify_cmd_port = dev_info.notify_cmd_port;
     pdev->notify_cursor_port = dev_info.notify_cursor_port;
-    pdev->notify_oom_port = dev_info.notify_oom_port;
+
+    pdev->asyncable[ASYNCABLE_UPDATE_AREA][ASYNC] = dev_info.update_area_async_port;
+    pdev->asyncable[ASYNCABLE_UPDATE_AREA][SYNC] = dev_info.update_area_port;
+    pdev->asyncable[ASYNCABLE_NOTIFY_OOM][ASYNC] = dev_info.notify_oom_async_port;
+    pdev->asyncable[ASYNCABLE_NOTIFY_OOM][SYNC] = dev_info.notify_oom_port;
+    pdev->asyncable[ASYNCABLE_MEMSLOT_ADD][ASYNC] = dev_info.memslot_add_async_port;
+    pdev->asyncable[ASYNCABLE_MEMSLOT_ADD][SYNC] = dev_info.memslot_add_port;
+    pdev->asyncable[ASYNCABLE_CREATE_PRIMARY][ASYNC] = dev_info.create_primary_async_port;
+    pdev->asyncable[ASYNCABLE_CREATE_PRIMARY][SYNC] = dev_info.create_primary_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_PRIMARY][ASYNC] = dev_info.destroy_primary_async_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_PRIMARY][SYNC] = dev_info.destroy_primary_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_SURFACE][ASYNC] = dev_info.destroy_surface_async_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_SURFACE][SYNC] = dev_info.destroy_surface_wait_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_ALL_SURFACES][ASYNC] = dev_info.destroy_all_surfaces_async_port;
+    pdev->asyncable[ASYNCABLE_DESTROY_ALL_SURFACES][SYNC] = dev_info.destroy_all_surfaces_port;
+
     pdev->display_event = dev_info.display_event;
     pdev->cursor_event = dev_info.cursor_event;
     pdev->sleep_event = dev_info.sleep_event;
+    pdev->io_cmd_event = dev_info.io_cmd_event;
 #if (WINVER < 0x0501)
     pdev->WaitForEvent = dev_info.WaitForEvent;
 #endif
@@ -709,7 +725,6 @@ static BOOL PrepareHardware(PDev *pdev)
 
     pdev->dev_update_id = dev_info.update_id;
 
-    pdev->update_area_port = dev_info.update_area_port;
     pdev->update_area = dev_info.update_area;
     pdev->update_surface = dev_info.update_surface;
 
@@ -754,14 +769,8 @@ static BOOL PrepareHardware(PDev *pdev)
     pdev->fb_size = video_mem_Info.FrameBufferLength;
     pdev->fb_phys = dev_info.fb_phys;
 
-    pdev->destroy_surface_wait_port = dev_info.destroy_surface_wait_port;
-    pdev->destroy_all_surfaces_port = dev_info.destroy_all_surfaces_port;
-    pdev->memslot_add_port = dev_info.memslot_add_port;
     pdev->memslot_del_port = dev_info.memslot_del_port;
 
-    pdev->create_primary_port = dev_info.create_primary_port;
-    pdev->destroy_primary_port = dev_info.destroy_primary_port;
-
     pdev->primary_memory_start = dev_info.surface0_area;
     pdev->primary_memory_size = dev_info.surface0_area_size;
 
diff --git a/display/qxldd.h b/display/qxldd.h
index fcfa752..8494365 100644
--- a/display/qxldd.h
+++ b/display/qxldd.h
@@ -30,6 +30,7 @@
 #include "windef.h"
 #include "wingdi.h"
 #include "winddi.h"
+#include "ioaccess.h"
 #include "qxl_driver.h"
 #include "mspace.h"
 #if (WINVER < 0x0501)
@@ -158,6 +159,24 @@ enum {
     NUM_MSPACES,
 };
 
+enum {
+    SYNC = 0,
+    ASYNC = 1
+};
+
+typedef enum {
+    ASYNCABLE_UPDATE_AREA = 0,
+    ASYNCABLE_NOTIFY_OOM,
+    ASYNCABLE_MEMSLOT_ADD,
+    ASYNCABLE_CREATE_PRIMARY,
+    ASYNCABLE_DESTROY_PRIMARY,
+    ASYNCABLE_DESTROY_SURFACE,
+    ASYNCABLE_DESTROY_ALL_SURFACES,
+
+    ASYNCABLE_COUNT
+} asyncable_t;
+
+
 typedef struct PDev PDev;
 
 typedef struct DrawArea {
@@ -263,10 +282,10 @@ typedef struct PDev {
     QXLReleaseRing *release_ring;
     PUCHAR notify_cmd_port;
     PUCHAR notify_cursor_port;
-    PUCHAR notify_oom_port;
     PEVENT display_event;
     PEVENT cursor_event;
     PEVENT sleep_event;
+    PEVENT io_cmd_event;
 
     PUCHAR log_port;
     UINT8 *log_buf;
@@ -288,7 +307,6 @@ typedef struct PDev {
 
     UINT32 *dev_update_id;
 
-    PUCHAR update_area_port;
     QXLRect *update_area;
     UINT32 *update_surface;
 
@@ -302,12 +320,9 @@ typedef struct PDev {
     PQXLWaitForEvent WaitForEvent;
 #endif
 
-    PUCHAR create_primary_port;
-    PUCHAR destroy_primary_port;
-    PUCHAR destroy_surface_wait_port;
-    PUCHAR memslot_add_port;
+    PUCHAR asyncable[ASYNCABLE_COUNT][2];
+    HSEMAPHORE asyncable_sem;
     PUCHAR memslot_del_port;
-    PUCHAR destroy_all_surfaces_port;
 
     UINT8* primary_memory_start;
     UINT32 primary_memory_size;
@@ -398,4 +413,32 @@ static _inline RingItem *RingGetTail(PDev *pdev, Ring *ring)
     return ret;
 }
 
+#if (WINVER < 0x0501)
+#define WAIT_FOR_EVENT(pdev, event, timeout) (pdev)->WaitForEvent(event, timeout)
+#else
+#define WAIT_FOR_EVENT(pdev, event, timeout) EngWaitForSingleObject(event, timeout)
+#endif
+
+/* Write to an ioport. For some operations we support a new port that returns
+ * immediatly, and completion is signaled by an interrupt that sets io_cmd_event.
+ * If the pci_revision is >= QXL_REVISION_STABLE_V10, we support it, else do
+ * a regular ioport write.
+ */
+static _inline void async_io(PDev *pdev, asyncable_t op, UCHAR val)
+{
+    if (pdev->pci_revision >= QXL_REVISION_STABLE_V10) {
+        EngAcquireSemaphore(pdev->asyncable_sem);
+        WRITE_PORT_UCHAR(pdev->asyncable[op][ASYNC], val);
+        WAIT_FOR_EVENT(pdev, pdev->io_cmd_event, NULL);
+        EngReleaseSemaphore(pdev->asyncable_sem);
+        DEBUG_PRINT((pdev, 3, "finished async %d\n", (int)op));
+    } else {
+        if (pdev->asyncable[op][SYNC] == NULL) {
+            DEBUG_PRINT((pdev, 0, "ERROR: trying calling sync io on NULL port %d\n", op));
+        } else {
+            WRITE_PORT_UCHAR(pdev->asyncable[op][SYNC], val);
+        }
+    }
+}
+
 #endif
diff --git a/display/res.c b/display/res.c
index c69b600..a20b5ce 100644
--- a/display/res.c
+++ b/display/res.c
@@ -21,6 +21,7 @@
 
 #include <ddraw.h>
 #include <dxmini.h>
+#include "qxldd.h"
 #include "os_dep.h"
 #include "res.h"
 #include "ioaccess.h"
@@ -33,12 +34,6 @@
 #include "devioctl.h"
 #include "ntddvdeo.h"
 
-#if (WINVER < 0x0501)
-#define WAIT_FOR_EVENT(pdev, event, timeout) (pdev)->WaitForEvent(event, timeout)
-#else
-#define WAIT_FOR_EVENT(pdev, event, timeout) EngWaitForSingleObject(event, timeout)
-#endif
-
 static _inline QXLPHYSICAL PA(PDev *pdev, PVOID virt, UINT8 slot_id)
 {
     PMemSlot *p_slot = &pdev->mem_slots[slot_id];
@@ -223,6 +218,11 @@ static void QXLSleep(PDev* pdev, int msec)
     DEBUG_PRINT((pdev, 19, "%s: 0x%lx exit\n", __FUNCTION__, pdev));
 }
 
+static void NotifyOOM(PDev *pdev)
+{
+    async_io(pdev, ASYNCABLE_NOTIFY_OOM, 0);
+}
+
 /* Called with malloc_sem held */
 static void WaitForReleaseRing(PDev* pdev)
 {
@@ -238,7 +238,7 @@ static void WaitForReleaseRing(PDev* pdev)
             if (!SPICE_RING_IS_EMPTY(pdev->release_ring)) {
                 break;
             }
-            WRITE_PORT_UCHAR(pdev->notify_oom_port, 0);
+            NotifyOOM(pdev);
         }
         SPICE_RING_CONS_WAIT(pdev->release_ring, wait);
 
@@ -262,8 +262,7 @@ static void WaitForReleaseRing(PDev* pdev)
                          pdev->Res->num_glyphs_pages,
                          pdev->Res->num_cursor_pages));
 #endif
-            //oom
-            WRITE_PORT_UCHAR(pdev->notify_oom_port, 0);
+            NotifyOOM(pdev);
         }
     }
     DEBUG_PRINT((pdev, 16, "%s: 0x%lx, done\n", __FUNCTION__, pdev));
@@ -2538,7 +2537,7 @@ void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id)
     DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
     CopyRect(pdev->update_area, area);
     *pdev->update_surface = surface_id;
-    WRITE_PORT_UCHAR(pdev->update_area_port, 0);
+    async_io(pdev, ASYNCABLE_UPDATE_AREA, 0);
 }
 
 #endif
@@ -3273,6 +3272,13 @@ BOOL ResInit(PDev *pdev)
     }
     pdev->quic_data = usr_data;
     pdev->quic_data_sem = EngCreateSemaphore();
+    if (!pdev->quic_data_sem) {
+        PANIC(pdev, "quic_data_sem creation failed\n");
+    }
+    pdev->asyncable_sem = EngCreateSemaphore();
+    if (!pdev->asyncable_sem) {
+        PANIC(pdev, "asyncable_sem creation failed\n");
+    }
 
     return TRUE;
 }
diff --git a/include/qxl_driver.h b/include/qxl_driver.h
index 9827a13..f9a31f1 100644
--- a/include/qxl_driver.h
+++ b/include/qxl_driver.h
@@ -54,9 +54,17 @@ typedef struct QXLDriverInfo {
     PUCHAR notify_cmd_port;
     PUCHAR notify_cursor_port;
     PUCHAR notify_oom_port;
+    PUCHAR update_area_async_port;
+    PUCHAR notify_oom_async_port;
+    PUCHAR memslot_add_async_port;
+    PUCHAR create_primary_async_port;
+    PUCHAR destroy_primary_async_port;
+    PUCHAR destroy_surface_async_port;
+    PUCHAR destroy_all_surfaces_async_port;
     PEVENT display_event;
     PEVENT cursor_event;
     PEVENT sleep_event;
+    PEVENT io_cmd_event;
 
     UINT32 num_pages;
     void *io_pages_virt;
diff --git a/miniport/qxl.c b/miniport/qxl.c
index f3ee090..e941206 100644
--- a/miniport/qxl.c
+++ b/miniport/qxl.c
@@ -86,6 +86,7 @@ typedef struct QXLExtension {
     PEVENT display_event;
     PEVENT cursor_event;
     PEVENT sleep_event;
+    PEVENT io_cmd_event;
 
     MemSlot *mem_slots;
 
@@ -520,6 +521,10 @@ void DevExternsionCleanup(QXLExtension *dev)
         VideoPortDeleteEvent(dev, dev->display_event);
     }
 
+    if (dev->io_cmd_event) {
+        VideoPortDeleteEvent(dev, dev->io_cmd_event);
+    }
+
     if (dev->rom) {
         VideoPortUnmapMemory(dev, dev->rom, NULL);
     }
@@ -551,6 +556,7 @@ VP_STATUS FindAdapter(PVOID dev_extension,
     PEVENT display_event = NULL;
     PEVENT cursor_event = NULL;
     PEVENT sleep_event = NULL;
+    PEVENT io_cmd_event = NULL;
 #if (WINVER >= 0x0501)
     VPOSVERSIONINFO  sys_info;
 #endif
@@ -602,9 +608,19 @@ VP_STATUS FindAdapter(PVOID dev_extension,
         return status;
     }
 
+    if ((status = VideoPortCreateEvent(dev_ext, 0, NULL, &io_cmd_event)) != NO_ERROR) {
+        DEBUG_PRINT((0,  "%s: create io_cmd event failed %lu\n",
+                     __FUNCTION__, status));
+        VideoPortDeleteEvent(dev_ext, sleep_event);
+        VideoPortDeleteEvent(dev_ext, display_event);
+        VideoPortDeleteEvent(dev_ext, cursor_event);
+        return status;
+    }
+
     dev_ext->display_event = display_event;
     dev_ext->cursor_event = cursor_event;
     dev_ext->sleep_event = sleep_event;
+    dev_ext->io_cmd_event = io_cmd_event;
 
     if ((status = Prob(dev_ext, conf_info, ranges, QXL_PCI_RANGES)) != NO_ERROR ||
         (status = InitIO(dev_ext, &ranges[QXL_IO_RANGE_INDEX])) != NO_ERROR ||
@@ -948,12 +964,24 @@ BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
             driver_info->display_event = dev_ext->display_event;
             driver_info->cursor_event = dev_ext->cursor_event;
             driver_info->sleep_event = dev_ext->sleep_event;
+            driver_info->io_cmd_event = dev_ext->io_cmd_event;
             driver_info->cmd_ring = &dev_ext->ram_header->cmd_ring;
             driver_info->cursor_ring = &dev_ext->ram_header->cursor_ring;
             driver_info->release_ring = &dev_ext->ram_header->release_ring;
             driver_info->notify_cmd_port = dev_ext->io_port + QXL_IO_NOTIFY_CMD;
             driver_info->notify_cursor_port = dev_ext->io_port + QXL_IO_NOTIFY_CURSOR;
             driver_info->notify_oom_port = dev_ext->io_port + QXL_IO_NOTIFY_OOM;
+            driver_info->update_area_async_port = dev_ext->io_port + QXL_IO_UPDATE_AREA_ASYNC;
+            driver_info->notify_oom_async_port = dev_ext->io_port + QXL_IO_NOTIFY_OOM_ASYNC;
+            driver_info->memslot_add_async_port = dev_ext->io_port + QXL_IO_MEMSLOT_ADD_ASYNC;
+            driver_info->create_primary_async_port =
+                dev_ext->io_port + QXL_IO_CREATE_PRIMARY_ASYNC;
+            driver_info->destroy_primary_async_port =
+                dev_ext->io_port + QXL_IO_DESTROY_PRIMARY_ASYNC;
+            driver_info->destroy_surface_async_port =
+                dev_ext->io_port + QXL_IO_DESTROY_SURFACE_ASYNC;
+            driver_info->destroy_all_surfaces_async_port =
+                dev_ext->io_port + QXL_IO_DESTROY_ALL_SURFACES_ASYNC;
 
             driver_info->log_port = dev_ext->io_port + QXL_IO_LOG;
             driver_info->log_buf = dev_ext->ram_header->log_buf;
@@ -1023,9 +1051,13 @@ VOID InterruptCallback(PVOID dev_extension, PVOID Context)
 
     if (pending & QXL_INTERRUPT_DISPLAY) {
         VideoPortSetEvent(dev_ext, dev_ext->display_event);
-    } if (pending & QXL_INTERRUPT_CURSOR) {
+    }
+    if (pending & QXL_INTERRUPT_CURSOR) {
         VideoPortSetEvent(dev_ext, dev_ext->cursor_event);
     }
+    if (pending & QXL_INTERRUPT_IO_CMD) {
+        VideoPortSetEvent(dev_ext, dev_ext->io_cmd_event);
+    }
 
     dev_ext->ram_header->int_mask = ~0;
     VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_UPDATE_IRQ, 0);
-- 
1.7.5.4



More information about the Spice-devel mailing list