[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 5/6 v2] gl: virtio-serial port driver for OpenGL pas
From: |
Andrzej Zaborowski |
Subject: |
[Qemu-devel] [PATCH 5/6 v2] gl: virtio-serial port driver for OpenGL passthrough. |
Date: |
Thu, 23 Feb 2012 00:22:48 +0100 |
This is a relatively simple to use OpenGL passthrough transport
because it doesn't need any guest kernel changes, but it's not
blazing fast. Still faster than software rendering.
The main problems are:
* transfers are split in very small chunks by virtio queues, some
OpenGL calls (think textures etc.) will take thousands of context
switches, vmenters/vmexits, sysenters/sysexits and buffer copying.
This should really be a single big zerocopy transfer.
* Linux virtio-serial allows only one process to have the port opened,
so applications on the guest have to fight over the port access
and open/close the device at every command buffer.
* host cannot know for sure when a guest process has died unexpectedly.
Signed-off-by: Andrzej Zaborowski <address@hidden>
---
v2: rebase after QOM.
---
Makefile.target | 3 +-
hw/virtio-gl-port.c | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 222 insertions(+), 1 deletions(-)
create mode 100644 hw/virtio-gl-port.c
diff --git a/Makefile.target b/Makefile.target
index 90e2739..3fae6ec 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -199,7 +199,8 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o
balloon.o ioport.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o
-obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
virtio-serial-bus.o
+obj-virtio-$(CONFIG_GL) += virtio-gl-port.o
+obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
virtio-serial-bus.o $(obj-virtio-y)
obj-y += vhost_net.o
obj-$(CONFIG_VHOST_NET) += vhost.o
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
diff --git a/hw/virtio-gl-port.c b/hw/virtio-gl-port.c
new file mode 100644
index 0000000..dbeaa2e
--- /dev/null
+++ b/hw/virtio-gl-port.c
@@ -0,0 +1,220 @@
+/*
+ * GL passthrough virtio-serial-based transport
+ *
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "qemu-error.h"
+#include "virtio-serial.h"
+
+#include "gl/vmgl.h"
+
+#define CMD_HEADER_SIZE 12
+
+static uint8_t *cmd_buffer;
+static size_t cmd_buffer_bytes;
+static size_t cmd_buffer_size;
+static const uint8_t *ret_buffer;
+static size_t ret_buffer_bytes;
+
+static size_t virtio_gl_get_cmd_size(const uint8_t *buf)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ return be32_to_cpup((const uint32_t *) buf);
+#else
+ return le32_to_cpup((const uint32_t *) buf);
+#endif
+}
+
+static size_t virtio_gl_get_buffer_size(const uint8_t *buf)
+{
+ return virtio_gl_get_cmd_size(buf + 4);
+}
+
+static void virtio_gl_guest_ready(VirtIOSerialPort *port)
+{
+ ssize_t ret;
+
+ if (!ret_buffer_bytes) {
+ return;
+ }
+
+ ret = virtio_serial_write(port, ret_buffer, ret_buffer_bytes);
+ if (ret < 0) {
+ error_report("%s: virtio_serial_write: error %i\n", __func__,
+ (int) -ret);
+ return;
+ }
+ ret_buffer += ret;
+ ret_buffer_bytes -= ret;
+}
+
+static void virtio_gl_handle_cmd(VirtIOSerialPort *port, const uint8_t *cmd)
+{
+ int pid = le32_to_cpup((const uint32_t *) cmd);
+ int in_size = virtio_gl_get_cmd_size(cmd + 4);
+ int ret;
+ ProcessStruct *process = vmgl_get_process(pid);
+
+ ret_buffer_bytes = virtio_gl_get_cmd_size(cmd + 8);
+ if (cmd_buffer_size < ret_buffer_bytes) {
+ cmd_buffer_size = ret_buffer_bytes;
+ cmd_buffer = g_realloc(cmd_buffer, cmd_buffer_size);
+ }
+ ret_buffer = cmd_buffer;
+
+ ret = vmgl_decode_call(process, cmd + CMD_HEADER_SIZE,
+ in_size - CMD_HEADER_SIZE, cmd_buffer + 4);
+ if (!ret) {
+ vmgl_disconnect(process);
+ return;
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ cpu_to_be32wu((uint32_t *) ret_buffer, ret);
+#else
+ cpu_to_le32wu((uint32_t *) ret_buffer, ret);
+#endif
+ /* FIXME: passing the buffer to decode_call_int and calling
+ * virtio_serial_write precludes zero-copy, figure out something
+ * better. */
+ virtio_gl_guest_ready(port);
+}
+
+static void virtio_gl_guest_close(VirtIOSerialPort *port)
+{
+ cmd_buffer_bytes = 0;
+ ret_buffer_bytes = 0;
+}
+
+/* Callback function that's called when the guest sends us data */
+static ssize_t virtio_gl_have_data(VirtIOSerialPort *port,
+ const uint8_t *buf, size_t len)
+{
+ size_t cmd_size;
+
+ ret_buffer_bytes = 0;
+
+ if (cmd_buffer_bytes) {
+ if (cmd_buffer_size < len + cmd_buffer_bytes) {
+ cmd_buffer_size = len + cmd_buffer_bytes;
+ cmd_buffer = g_realloc(cmd_buffer, cmd_buffer_size);
+ }
+
+ memcpy(cmd_buffer + cmd_buffer_bytes, buf, len);
+ cmd_buffer_bytes += len;
+ if (cmd_buffer_bytes < CMD_HEADER_SIZE) {
+ return len;
+ }
+
+ cmd_size = virtio_gl_get_buffer_size(cmd_buffer);
+ if (cmd_buffer_bytes > cmd_size) {
+ error_report("virtio-gl-port: received buffer is too big");
+ cmd_buffer_bytes = 0;
+ return -EINVAL;
+ }
+ if (cmd_buffer_bytes < cmd_size) {
+ return len;
+ }
+ buf = cmd_buffer;
+ cmd_buffer_bytes = 0;
+ } else if (len < CMD_HEADER_SIZE ||
+ len < (cmd_size = virtio_gl_get_buffer_size(buf))) {
+ if (len < CMD_HEADER_SIZE) {
+ cmd_size = len;
+ }
+ if (cmd_size > cmd_buffer_size) {
+ cmd_buffer = g_realloc(cmd_buffer, cmd_size);
+ cmd_buffer_size = cmd_size;
+ }
+
+ if (len > cmd_size) {
+ error_report("virtio-gl-port: received buffer too big");
+ return -EINVAL;
+ }
+
+ cmd_buffer_bytes = len;
+ memcpy(cmd_buffer, buf, len);
+ return len;
+ }
+
+ virtio_gl_handle_cmd(port, buf);
+
+ return len;
+}
+
+static int virtio_gl_port_initfn(VirtIOSerialPort *port)
+{
+ static int exists;
+
+ if (vmgl_acceleration_capability_check() != 0) {
+ error_report("virtio-gl-port: Host GL capabilities check failed, "
+ "device won't be created\n");
+ return -1;
+ }
+
+ if (exists++) {
+ error_report("virtio-gl-port: Only one device makes sense\n");
+ return -1;
+ }
+
+ /* The guest identifies "OpenGL passthrough"-capable serial ports
+ * from regular consoles using this name. */
+ port->name = (char *) "qemugl";
+
+ virtio_serial_open(port);
+
+ return 0;
+}
+
+static int virtio_gl_port_exitfn(VirtIOSerialPort *port)
+{
+ virtio_serial_close(port);
+
+ if (cmd_buffer) {
+ g_free(cmd_buffer);
+ cmd_buffer = NULL;
+ cmd_buffer_size = 0;
+ cmd_buffer_bytes = 0;
+ }
+
+ return 0;
+}
+
+static void virtio_gl_port_class_init(ObjectClass *klass, void *data)
+{
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
+
+ k->init = virtio_gl_port_initfn,
+ k->exit = virtio_gl_port_exitfn,
+ k->have_data = virtio_gl_have_data,
+ k->guest_ready = virtio_gl_guest_ready;
+ k->guest_close = virtio_gl_guest_close;
+}
+
+static TypeInfo virtio_gl_port_info = {
+ .name = "virtio-gl-port",
+ .parent = TYPE_VIRTIO_SERIAL_PORT,
+ .instance_size = sizeof(VirtIOSerialPort),
+ .class_init = virtio_gl_port_class_init,
+};
+
+static void virtio_gl_port_register_types(void)
+{
+ type_register_static(&virtio_gl_port_info);
+}
+type_init(virtio_gl_port_register_types)
--
1.7.4.4