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 <andrew.zaborow...@intel.com> --- 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