summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2013-08-12 22:23:54 +0300
committerAlon Levy <alevy@redhat.com>2013-08-14 18:51:16 +0300
commit0d9f6207b01a8389af085e8137678b58a791612a (patch)
tree177ee9eca6691811145109765ae6d07aca051d60
parentc5be178aa96077eeefd2dde677a0254e256cb2e1 (diff)
hw/display/qxl: support shared memorywip/shared-memory
Requires spice server with qxl interface 3.4, available in spice-server 0.12.5 ; doesn't bump the required spice-server, the feature is disabled while the new spice boolean property "shared-memory" remains but an early exit triggers if it is set. Shared memory support means that locally run spice client can opt to receive damage events instead of rendering commands, and all rendering is done in the qemu process. The shared memory is allocated for all bars plus the shadow buffer used by ui/spice-display.c in one allocation, since the protocol allows for a single shared file with offsets for each surface. Layout used: offset ====== .--------. 0 | VGA | vga.vram_size | VRAM | vga.vram_size + vram_size | shadow | '--------'
-rw-r--r--hw/display/qxl.c143
-rw-r--r--hw/display/qxl.h8
-rw-r--r--include/ui/spice-display.h10
-rw-r--r--trace-events5
-rw-r--r--ui/spice-display.c41
5 files changed, 183 insertions, 24 deletions
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 861266f7d..ad3560a2e 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -19,6 +19,7 @@
*/
#include <zlib.h>
+#include <sys/mman.h>
#include "qemu-common.h"
#include "qemu/timer.h"
@@ -1076,6 +1077,19 @@ static int interface_client_monitors_config(QXLInstance *sin,
return 1;
}
+#if SPICE_INTERFACE_QXL_MAJOR > 3 || \
+ (SPICE_INTERFACE_QXL_MAJOR == 3 && SPICE_INTERFACE_QXL_MINOR >= 4)
+static const char *interface_shared_memory_file_name(QXLInstance *sin)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+ if (qxl->shm.name[0] != 0) {
+ return (const char *)qxl->shm.name;
+ }
+ return NULL;
+}
+#endif
+
static const QXLInterface qxl_interface = {
.base.type = SPICE_INTERFACE_QXL,
.base.description = "qxl gpu",
@@ -1099,6 +1113,10 @@ static const QXLInterface qxl_interface = {
.update_area_complete = interface_update_area_complete,
.set_client_capabilities = interface_set_client_capabilities,
.client_monitors_config = interface_client_monitors_config,
+#if SPICE_INTERFACE_QXL_MAJOR > 3 || \
+ (SPICE_INTERFACE_QXL_MAJOR == 3 && SPICE_INTERFACE_QXL_MINOR >= 4)
+ .shared_memory_file_name = interface_shared_memory_file_name,
+#endif
};
static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -1382,11 +1400,12 @@ static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl)
static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
qxl_async_io async)
{
- QXLDevSurfaceCreate surface;
+ QXLDevSurfaceCreate surface = {0};
QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
int size;
int requested_height = le32_to_cpu(sc->height);
int requested_stride = le32_to_cpu(sc->stride);
+ intptr_t shm_offset;
size = abs(requested_stride) * requested_height;
if (size > qxl->vgamem_size) {
@@ -1410,11 +1429,20 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
surface.width = le32_to_cpu(sc->width);
surface.type = le32_to_cpu(sc->type);
surface.flags = le32_to_cpu(sc->flags);
+
+ if (qxl->shm.use) {
+ shm_offset = (intptr_t)((uint8_t *)qxl_phys2virt(
+ qxl, surface.mem, MEMSLOT_GROUP_GUEST)
+ - qxl->vga.vram_ptr);
+ } else {
+ shm_offset = 0;
+ }
trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
sc->format, sc->position);
trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
- sc->flags);
-
+ sc->flags, shm_offset);
+ fprintf(stderr, "%s: shm_offset = %ld (mem = %ld)\n", __func__,
+ shm_offset, surface.mem);
if ((surface.stride & 0x3) != 0) {
qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0",
surface.stride);
@@ -1429,7 +1457,8 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
qxl->mode = QXL_MODE_NATIVE;
qxl->cmdflags = 0;
- qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
+ qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async,
+ shm_offset);
if (async == QXL_SYNC) {
qxl_create_guest_primary_complete(qxl);
@@ -1995,12 +2024,48 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl)
qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
}
+/* A note about shared memory layout:
+ *
+ * Spice's QXL interface supports a single shared memory file, with each
+ * primary carrying an offset into it. The layout we use is:
+ *
+ * offset
+ * ====== .--------.
+ * 0 | VGA |
+ * vga.vram_size | VRAM |
+ * vga.vram_size + vram_size | shadow |
+ * '--------'
+ */
+static void *qxl_shm_vram_ptr(PCIQXLDevice *qxl)
+{
+ return ((uint8_t *)qxl->shm.ptr) + qxl->vga.vram_size;
+}
+
+static void *qxl_shm_vga_ptr(PCIQXLDevice *qxl)
+{
+ return ((uint8_t *)qxl->shm.ptr) + qxl->vga.vram_size + qxl->vram_size;
+}
+
+static uint64_t qxl_shm_vga_offset(PCIQXLDevice *qxl)
+{
+ return qxl->vga.vram_size + qxl->vram_size;
+}
+
static int qxl_init_common(PCIQXLDevice *qxl)
{
uint8_t* config = qxl->pci.config;
uint32_t pci_device_rev;
uint32_t io_size;
+#if SPICE_INTERFACE_QXL_MAJOR < 3 || \
+ (SPICE_INTERFACE_QXL_MAJOR == 3 && SPICE_INTERFACE_QXL_MINOR < 4)
+ if (qxl->shm.use) {
+ error_report("qemu was compiled against a too old spice server with "
+ "qxl interface %d.%d < 3.4 to support shared memory.",
+ SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR);
+ return -1;
+ }
+#endif
qxl->mode = QXL_MODE_UNDEFINED;
qxl->generation = 1;
qxl->num_memslots = NUM_MEMSLOTS;
@@ -2048,8 +2113,13 @@ static int qxl_init_common(PCIQXLDevice *qxl)
init_qxl_ram(qxl);
qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces);
- memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
- qxl->vram_size);
+ if (qxl->shm.use) {
+ memory_region_init_ram_ptr(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
+ qxl->vram_size, qxl_shm_vram_ptr(qxl));
+ } else {
+ memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
+ qxl->vram_size);
+ }
vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32",
&qxl->vram_bar, 0, qxl->vram32_size);
@@ -2119,17 +2189,61 @@ static const GraphicHwOps qxl_ops = {
.text_update = qxl_hw_text_update,
};
+static void *qxl_init_shm(PCIQXLDevice *qxl, int shm_size)
+{
+ void *ptr;
+
+#ifdef DEBUG_SHMEM_NO_RANDOM
+ snprintf((char *)qxl->shm.name, sizeof(qxl->shm.name),
+ "spice.primary.debug");
+#else
+ snprintf((char *)qxl->shm.name, sizeof(qxl->shm.name),
+ "qemu.qxl.%06d.%08lx%08lx%08lx%08lx", getpid(),
+ random(), random(), random(), random());
+#endif
+ trace_qxl_shared_memory((char *)qxl->shm.name);
+ qxl->shm.fd = shm_open((char *)qxl->shm.name,
+ O_CREAT|O_RDWR|O_EXCL,
+ S_IRWXU|S_IRWXG|S_IRWXO);
+ if (qxl->shm.fd <= 0) {
+ fprintf(stderr, "qxl: failed to allocate shared memory");
+ exit(-1);
+ }
+ if (ftruncate(qxl->shm.fd, shm_size) != 0) {
+ fprintf(stderr, "qxl: failed to ftruncate shared memory to %d",
+ qxl->vga.vram_size);
+ exit(-1);
+ }
+ ptr = mmap(0, shm_size, PROT_READ|PROT_WRITE, MAP_SHARED, qxl->shm.fd, 0);
+ if (!ptr) {
+ fprintf(stderr, "qxl: failed to mmap the shared memory file");
+ exit(-1);
+ }
+ qxl->shm.ptr = ptr;
+ return ptr;
+}
+
static int qxl_init_primary(PCIDevice *dev)
{
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
VGACommonState *vga = &qxl->vga;
PortioList *qxl_vga_port_list = g_new(PortioList, 1);
int rc;
+ void *ptr;
+ uint8_t *common_buf;
qxl->id = 0;
qxl_init_ramsize(qxl);
vga->vram_size_mb = qxl->vga.vram_size >> 20;
- vga_common_init(vga, OBJECT(dev));
+ if (qxl->shm.use) {
+ ptr = qxl_init_shm(qxl, qxl->vga.vram_size + qxl->vram_size
+ + SPICE_SIMPLE_DISPLAY_BUFFER_SIZE);
+ vga_common_init_ptr(vga, OBJECT(dev), ptr);
+ common_buf = qxl_shm_vga_ptr(qxl);
+ } else {
+ vga_common_init(vga, OBJECT(dev));
+ common_buf = NULL;
+ }
vga_init(vga, OBJECT(dev),
pci_address_space(dev), pci_address_space_io(dev), false);
portio_list_init(qxl_vga_port_list, OBJECT(dev), qxl_vga_portio_list,
@@ -2137,7 +2251,8 @@ static int qxl_init_primary(PCIDevice *dev)
portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
vga->con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl);
- qemu_spice_display_init_common(&qxl->ssd);
+ qemu_spice_display_init_common(&qxl->ssd, common_buf,
+ qxl_shm_vga_offset(qxl));
rc = qxl_init_common(qxl);
if (rc != 0) {
@@ -2154,11 +2269,18 @@ static int qxl_init_secondary(PCIDevice *dev)
{
static int device_id = 1;
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+ void *ptr;
qxl->id = device_id++;
qxl_init_ramsize(qxl);
- memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
- qxl->vga.vram_size);
+ if (qxl->shm.use) {
+ ptr = qxl_init_shm(qxl, qxl->vga.vram_size + qxl->vram_size);
+ memory_region_init_ram_ptr(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
+ qxl->vga.vram_size, ptr);
+ } else {
+ memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
+ qxl->vga.vram_size);
+ }
vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
qxl->vga.con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl);
@@ -2368,6 +2490,7 @@ static Property qxl_properties[] = {
DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
+ DEFINE_PROP_BOOL("shared_memory", PCIQXLDevice, shm.use, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index 8da3f986d..a4c8eb496 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -118,6 +118,14 @@ typedef struct PCIQXLDevice {
int num_dirty_rects;
QXLRect dirty[QXL_NUM_DIRTY_RECTS];
QEMUBH *update_area_bh;
+
+ /* shared memory state */
+ struct {
+ bool use; /* set via command line parameter */
+ int fd;
+ uint8_t name[128];
+ void *ptr;
+ } shm;
} PCIQXLDevice;
#define PANIC_ON(x) if ((x)) { \
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index a46bc8001..892d2de01 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -32,6 +32,8 @@
#define MEMSLOT_GROUP_GUEST 1
#define NUM_MEMSLOTS_GROUPS 2
+#define SPICE_SIMPLE_DISPLAY_BUFFER_SIZE (16 * 1024 * 1024)
+
/*
* Internal enum to differenciate between options for
* io calls that have a sync (old) version and an _async (new)
@@ -94,6 +96,8 @@ struct SimpleSpiceDisplay {
QTAILQ_HEAD(, SimpleSpiceUpdate) updates;
QEMUCursor *cursor;
int mouse_x, mouse_y;
+
+ size_t shm_offset;
};
struct SimpleSpiceUpdate {
@@ -113,7 +117,8 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
void qemu_spice_vm_change_state_handler(void *opaque, int running,
RunState state);
-void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd);
+void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, uint8_t *buf,
+ uint64_t shm_offset);
void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
int x, int y, int w, int h);
@@ -128,7 +133,8 @@ void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid,
uint32_t sid);
void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
QXLDevSurfaceCreate *surface,
- qxl_async_io async);
+ qxl_async_io async,
+ uint64_t shm_offset);
void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
uint32_t id, qxl_async_io async);
void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
diff --git a/trace-events b/trace-events
index 43f7e2ff4..acd58f689 100644
--- a/trace-events
+++ b/trace-events
@@ -1042,7 +1042,7 @@ migration_throttle(void) ""
disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"
disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u"
qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=%" PRIx64 " %u,%u"
-qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags) "%d %d,%d,%d"
+qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags, uint64_t offset) "%d %d,%d,%d,%ld"
qxl_destroy_primary(int qid) "%d"
qxl_enter_vga_mode(int qid) "%d"
qxl_exit_vga_mode(int qid) "%d"
@@ -1099,11 +1099,12 @@ qxl_client_monitors_config_unsupported_by_device(int qid, int revision) "%d revi
qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d"
qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u"
qxl_set_client_capabilities_unsupported_by_revision(int qid, int revision) "%d revision=%d"
+qxl_shared_memory(char *name) "filename=%s"
# ui/spice-display.c
qemu_spice_add_memslot(int qid, uint32_t slot_id, unsigned long virt_start, unsigned long virt_end, int async) "%d %u: host virt 0x%lx - 0x%lx async=%d"
qemu_spice_del_memslot(int qid, uint32_t gid, uint32_t slot_id) "%d gid=%u sid=%u"
-qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int async) "%d sid=%u surface=%p async=%d"
+qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int async, uint64_t shm_offset) "%d sid=%u surface=%p async=%d shm_offset=%ld"
qemu_spice_destroy_primary_surface(int qid, uint32_t sid, int async) "%d sid=%u async=%d"
qemu_spice_wakeup(uint32_t qid) "%d"
qemu_spice_create_update(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "lr %d -> %d, tb -> %d -> %d"
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 82d8b9f9a..8f99e2ed6 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -95,15 +95,29 @@ void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
QXLDevSurfaceCreate *surface,
- qxl_async_io async)
+ qxl_async_io async,
+ uint64_t shm_offset)
{
- trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async);
+ uint64_t cookie;
+
+ trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async,
+ shm_offset);
if (async != QXL_SYNC) {
- spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface,
- (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
- QXL_IO_CREATE_PRIMARY_ASYNC));
+ cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+ QXL_IO_CREATE_PRIMARY_ASYNC);
+#if SPICE_SERVER_VERSION >= 0x000c05
+ spice_qxl_create_primary_surface_shm(&ssd->qxl, id, surface, cookie,
+ shm_offset);
+#else
+ spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, cookie);
+#endif
} else {
+#if SPICE_SERVER_VERSION >= 0x000c05
+ spice_qxl_create_primary_surface_shm(&ssd->qxl, id, surface, 0,
+ shm_offset);
+#else
ssd->worker->create_primary_surface(ssd->worker, id, surface);
+#endif
}
}
@@ -324,7 +338,8 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
surface.mem = (uintptr_t)ssd->buf;
surface.group_id = MEMSLOT_GROUP_HOST;
- qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
+ qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC,
+ ssd->shm_offset);
}
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
@@ -334,7 +349,8 @@ void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
}
-void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd)
+void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, uint8_t *buf,
+ uint64_t shm_offset)
{
qemu_mutex_init(&ssd->lock);
QTAILQ_INIT(&ssd->updates);
@@ -343,8 +359,13 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd)
if (ssd->num_surfaces == 0) {
ssd->num_surfaces = 1024;
}
- ssd->bufsize = (16 * 1024 * 1024);
- ssd->buf = g_malloc(ssd->bufsize);
+ ssd->bufsize = SPICE_SIMPLE_DISPLAY_BUFFER_SIZE;
+ if (buf) {
+ ssd->buf = buf;
+ } else {
+ ssd->buf = g_malloc(ssd->bufsize);
+ }
+ ssd->shm_offset = shm_offset;
}
/* display listener callbacks */
@@ -615,7 +636,7 @@ void qemu_spice_display_init(DisplayState *ds)
{
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
- qemu_spice_display_init_common(ssd);
+ qemu_spice_display_init_common(ssd, NULL, 0);
ssd->qxl.base.sif = &dpy_interface.base;
qemu_spice_add_interface(&ssd->qxl.base);