[Spice-devel] [RFCv2 16/21] xspice: add display interface

Alon Levy alevy at redhat.com
Fri Apr 29 02:49:54 PDT 2011


---
 src/Makefile.am        |    1 +
 src/qxl.h              |   24 ++++
 src/qxl_driver.c       |    3 +
 src/spiceqxl_display.c |  319 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/spiceqxl_display.h |   11 ++
 5 files changed, 358 insertions(+), 0 deletions(-)
 create mode 100644 src/spiceqxl_display.c
 create mode 100644 src/spiceqxl_display.h

diff --git a/src/Makefile.am b/src/Makefile.am
index eaa0746..5eedc93 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,6 +62,7 @@ spiceqxl_drv_la_SOURCES =				\
 	spiceqxl_io_port.c			\
 	spiceqxl_driver.c			\
 	spiceqxl_main_loop.c			\
+	spiceqxl_display.c			\
 	qxl_driver.c				\
 	qxl_image.c				\
 	qxl_surface.c				\
diff --git a/src/qxl.h b/src/qxl.h
index d7460f3..0ee7033 100644
--- a/src/qxl.h
+++ b/src/qxl.h
@@ -190,6 +190,26 @@ struct _qxl_screen_t
     /* XSpice specific */
     struct QXLRom		shadow_rom;    /* Parameter RAM */
     SpiceServer *       spice_server;
+    QXLWorker *         worker;
+    QXLInstance         display_sin;
+    /* XSpice specific, dragged from the Device */
+    QXLReleaseInfo     *last_release;
+
+    uint32_t           cmdflags;
+    uint32_t           oom_running;
+    uint32_t           num_free_res; /* is having a release ring effective
+                                        for Xspice? */
+    /* This is only touched from red worker thread - do not access
+     * from Xorg threads. */
+    struct guest_primary {
+        QXLSurfaceCreate surface;
+        uint32_t       commands;
+        uint32_t       resized;
+        int32_t        stride;
+        uint32_t       bits_pp;
+        uint32_t       bytes_pp;
+        uint8_t        *data, *flipped;
+    } guest_primary;
 #endif /* XSPICE */
 };
 
@@ -380,9 +400,13 @@ static inline void ioport_write(qxl_screen_t *qxl, int port, int val)
 
 #ifdef XSPICE
 
+#define MEMSLOT_GROUP 0
+#define NUM_MEMSLOTS_GROUPS 1
+
 // Taken from qemu's qxl.c, not sure the values make sense? we
 // only have a single slot, and it is never changed after being added,
 // so not a problem?
+#define NUM_MEMSLOTS 8
 #define MEMSLOT_GENERATION_BITS 8
 #define MEMSLOT_SLOT_BITS 1
 
diff --git a/src/qxl_driver.c b/src/qxl_driver.c
index 938727e..fe2f532 100644
--- a/src/qxl_driver.c
+++ b/src/qxl_driver.c
@@ -40,6 +40,7 @@
 #ifdef XSPICE
 #include "spiceqxl_driver.h"
 #include "spiceqxl_main_loop.h"
+#include "spiceqxl_display.h"
 #endif /* XSPICE */
 
 #if 0
@@ -900,6 +901,8 @@ spiceqxl_screen_init(int scrnIndex, ScrnInfoPtr pScrn, qxl_screen_t *qxl)
         // TODO - parse rest of parameters (streaming, compression, jpeg, etc.) from config
         core = basic_event_loop_init();
         spice_server_init(qxl->spice_server, core);
+        qxl_add_spice_display_interface(qxl);
+        qxl->worker->start(qxl->worker);
     }
     qxl->spice_server = qxl->spice_server;
 }
diff --git a/src/spiceqxl_display.c b/src/spiceqxl_display.c
new file mode 100644
index 0000000..b26bbfb
--- /dev/null
+++ b/src/spiceqxl_display.c
@@ -0,0 +1,319 @@
+#include <spice.h>
+
+#include "qxl.h"
+#include "spiceqxl_display.h"
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+        const typeof(((type *) 0)->member) *__mptr = (ptr);     \
+        (type *) ((char *) __mptr - offsetof(type, member));})
+#endif
+
+/* TODO: these is copied from qemu/hw/qxl.c . It shouldn't be there
+ * either, these ugly undef just remove the definitions from spice-protocol/spice/ipc_ring.h
+ * What should happen is using one definition, or a rename, and both in spice-protocol (because
+ * all the others are there).
+ * Practically speaking the only difference between the two is extra checking in this version,
+ * and usage (this one takes an extra parameter, the previous is meant to be used by assignment) */
+#undef SPICE_RING_PROD_ITEM
+#define SPICE_RING_PROD_ITEM(r, ret) {                                  \
+        typeof(r) start = r;                                            \
+        typeof(r) end = r + 1;                                          \
+        uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r);           \
+        typeof(&(r)->items[prod]) m_item = &(r)->items[prod];           \
+        if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+            abort();                                                    \
+        }                                                               \
+        ret = &m_item->el;                                              \
+    }
+
+#undef SPICE_RING_CONS_ITEM
+#define SPICE_RING_CONS_ITEM(r, ret) {                                  \
+        typeof(r) start = r;                                            \
+        typeof(r) end = r + 1;                                          \
+        uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r);           \
+        typeof(&(r)->items[cons]) m_item = &(r)->items[cons];           \
+        if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+            abort();                                                    \
+        }                                                               \
+        ret = &m_item->el;                                              \
+    }
+
+
+
+/* XSpice:
+ * We only need a single identify slot, no need to change it for the lifetime
+ * We actually need no slots, but less changes if we leave one.
+ * We currently add it during attache_worker - should not be called more
+ * then once during lifetime (but we don't check)
+ */
+QXLDevMemSlot slot = {
+.slot_group_id = MEMSLOT_GROUP,
+.slot_id = 0,
+.generation = 0,
+.virt_start = 0,
+.virt_end = ~0,
+.addr_delta = 0,
+.qxl_ram_size = ~0,
+};
+
+// TODO - reall dprint, this is just to get it compiling
+#define dprint(qxl, lvl, fmt, ...) printf(fmt, __VA_ARGS__)
+
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+    static int count = 0;
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+
+    if (++count > 1) {
+        dprint(qxl, 0, "%s ignored\n", __FUNCTION__);
+        return;
+    }
+    dprint(qxl, 1, "%s:\n", __FUNCTION__);
+    qxl_worker->add_memslot(qxl_worker, &slot);
+    qxl->worker = qxl_worker;
+}
+
+static void interface_set_compression_level(QXLInstance *sin, int level)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+
+    dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level);
+    qxl->shadow_rom.compression_level = level;
+    qxl->rom->compression_level = level;
+}
+
+static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+
+    qxl->shadow_rom.mm_clock = mm_time;
+    qxl->rom->mm_clock = mm_time;
+}
+
+static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+
+    dprint(qxl, 1, "%s:\n", __FUNCTION__);
+    info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
+    info->memslot_id_bits = MEMSLOT_SLOT_BITS;
+    info->num_memslots = NUM_MEMSLOTS;
+    info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+    info->internal_groupslot_id = 0;
+    info->qxl_ram_size = qxl->shadow_rom.num_pages << TARGET_PAGE_BITS;
+    info->n_surfaces = NUM_SURFACES;
+}
+
+void qxl_send_events(qxl_screen_t *qxl, int events)
+{
+#if 0
+    ErrorF("qxl_send_events %d\n", events);
+    qxl_garbage_collect(qxl);
+#endif
+    /* we should trigger a garbage collection, but via a pipe. TODO */
+}
+
+/* called from spice server thread context only */
+static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    QXLRam *ram = get_ram_header(qxl);
+    QXLCommandRing *ring;
+    QXLCommand *cmd;
+    int notify;
+
+    dprint(qxl, 2, "%s: %s\n", __FUNCTION__,
+           qxl->cmdflags ? "compat" : "native");
+    ring = &ram->cmd_ring;
+    if (SPICE_RING_IS_EMPTY(ring)) {
+        return FALSE;
+    }
+    SPICE_RING_CONS_ITEM(ring, cmd);
+    ext->cmd      = *cmd;
+    ext->group_id = MEMSLOT_GROUP;
+    ext->flags    = qxl->cmdflags;
+    SPICE_RING_POP(ring, notify);
+    if (notify) {
+        qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
+    }
+    qxl->guest_primary.commands++;
+    // TODO: reenable, useful
+    //qxl_track_command(qxl, ext);
+    //qxl_log_command(qxl, "cmd", ext);
+    return TRUE;
+}
+
+/* called from spice server thread context only */
+static int interface_req_cmd_notification(QXLInstance *sin)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    QXLRam *header = get_ram_header(qxl);
+    int wait = 1;
+
+    SPICE_RING_CONS_WAIT(&header->cmd_ring, wait);
+    return wait;
+}
+
+/* called from spice server thread context only */
+static inline void qxl_push_free_res(qxl_screen_t *qxl, int flush)
+{
+    QXLRam *header = get_ram_header(qxl);
+    QXLReleaseRing *ring = &header->release_ring;
+    uint64_t *item;
+    int notify;
+
+#define QXL_FREE_BUNCH_SIZE 32
+
+    if (ring->prod - ring->cons + 1 == ring->num_items) {
+        /* ring full -- can't push */
+        return;
+    }
+    if (!flush && qxl->oom_running) {
+        /* collect everything from oom handler before pushing */
+        return;
+    }
+    if (!flush && qxl->num_free_res < QXL_FREE_BUNCH_SIZE) {
+        /* collect a bit more before pushing */
+        return;
+    }
+
+    SPICE_RING_PUSH(ring, notify);
+    dprint(qxl, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n",
+           qxl->num_free_res, notify ? "yes" : "no",
+           ring->prod - ring->cons, ring->num_items,
+           ring->prod, ring->cons);
+    if (notify) {
+        qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
+    }
+    SPICE_RING_PROD_ITEM(ring, item);
+    *item = 0;
+    qxl->num_free_res = 0;
+    qxl->last_release = NULL;
+}
+
+/* called from spice server thread context only */
+static void interface_release_resource(QXLInstance *sin,
+                                       struct QXLReleaseInfoExt ext)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    QXLRam *ram = get_ram_header(qxl);
+    QXLReleaseRing *ring;
+    uint64_t *item, id;
+
+    /*
+     * ext->info points into guest-visible memory
+     * pci bar 0, $command.release_info
+     */
+    ring = &ram->release_ring;
+    SPICE_RING_PROD_ITEM(ring, item);
+    if (*item == 0) {
+        /* stick head into the ring */
+        id = ext.info->id;
+        ext.info->next = 0;
+        *item = id;
+    } else {
+        /* append item to the list */
+        qxl->last_release->next = ext.info->id;
+        ext.info->next = 0;
+    }
+    qxl->last_release = ext.info;
+    qxl->num_free_res++;
+    dprint(qxl, 3, "%4d\r", qxl->num_free_res);
+    qxl_push_free_res(qxl, 0);
+}
+
+/* called from spice server thread context only */
+static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    QXLCursorRing *ring;
+    QXLCommand *cmd;
+    QXLRam *ram = get_ram_header(qxl);
+    int notify;
+
+    ring = &ram->cursor_ring;
+    if (SPICE_RING_IS_EMPTY(ring)) {
+        return FALSE;
+    }
+    SPICE_RING_CONS_ITEM(ring, cmd);
+    ext->cmd      = *cmd;
+    ext->group_id = MEMSLOT_GROUP;
+    ext->flags    = qxl->cmdflags;
+    SPICE_RING_POP(ring, notify);
+    if (notify) {
+        qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
+    }
+    qxl->guest_primary.commands++;
+    //qxl_track_command(qxl, ext); // TODO - copy me
+    //qxl_log_command(qxl, "csr", ext); // TODO - copy me
+    return TRUE;
+}
+
+/* called from spice server thread context only */
+static int interface_req_cursor_notification(QXLInstance *sin)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    QXLRam *ram = get_ram_header(qxl);
+    int wait = 1;
+
+    SPICE_RING_CONS_WAIT(&ram->cursor_ring, wait);
+    return wait;
+}
+
+/* called from spice server thread context */
+static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+{
+    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+    abort();
+}
+
+/* called from spice server thread context only */
+static int interface_flush_resources(QXLInstance *sin)
+{
+    qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin);
+    int ret;
+
+    dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res);
+    ret = qxl->num_free_res;
+    if (ret) {
+        qxl_push_free_res(qxl, 1);
+    }
+    return ret;
+}
+
+
+static const QXLInterface qxl_interface = {
+    .base.type               = SPICE_INTERFACE_QXL,
+    .base.description        = "qxl gpu",
+    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
+    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
+
+    .attache_worker          = interface_attach_worker,
+    .set_compression_level   = interface_set_compression_level,
+    .set_mm_time             = interface_set_mm_time,
+    .get_init_info           = interface_get_init_info,
+
+    /* the callbacks below are called from spice server thread context */
+    .get_command             = interface_get_command,
+    .req_cmd_notification    = interface_req_cmd_notification,
+    .release_resource        = interface_release_resource,
+    .get_cursor_command      = interface_get_cursor_command,
+    .req_cursor_notification = interface_req_cursor_notification,
+    .notify_update           = interface_notify_update,
+    .flush_resources         = interface_flush_resources,
+};
+
+void qxl_add_spice_display_interface(qxl_screen_t *qxl)
+{
+    /* use this function to initialize the parts of qxl_screen_t
+     * that were added directly from qemu/hw/qxl.c */
+    qxl->cmdflags = 0;
+    qxl->oom_running = 0;
+    qxl->num_free_res = 0;
+
+    qxl->display_sin.base.sif = &qxl_interface.base;
+    qxl->display_sin.id = 0;
+    qxl->display_sin.st = (struct QXLState*)qxl;
+    spice_server_add_interface(qxl->spice_server, &qxl->display_sin.base);
+}
diff --git a/src/spiceqxl_display.h b/src/spiceqxl_display.h
new file mode 100644
index 0000000..3194176
--- /dev/null
+++ b/src/spiceqxl_display.h
@@ -0,0 +1,11 @@
+#ifndef QXL_SPICE_DISPLAY_H
+#define QXL_SPICE_DISPLAY_H
+
+#include "qxl.h"
+#include <spice.h>
+
+void qxl_add_spice_display_interface(qxl_screen_t *qxl);
+/* spice-server to device, now spice-server to xspice */
+void qxl_send_events(qxl_screen_t *qxl, int events);
+
+#endif // QXL_SPICE_DISPLAY_H
-- 
1.7.4.4



More information about the Spice-devel mailing list