--- 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 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel