From patchwork Mon Jan 9 07:57:55 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrzej zaborowski X-Patchwork-Id: 135094 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CB396B6F65 for ; Tue, 10 Jan 2012 05:59:47 +1100 (EST) Received: from localhost ([::1]:60432 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RkKRT-0003p9-2X for incoming@patchwork.ozlabs.org; Mon, 09 Jan 2012 13:59:35 -0500 Received: from eggs.gnu.org ([140.186.70.92]:60085) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RkKRM-0003p2-I3 for qemu-devel@nongnu.org; Mon, 09 Jan 2012 13:59:30 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RkKRK-0006pq-P9 for qemu-devel@nongnu.org; Mon, 09 Jan 2012 13:59:28 -0500 Received: from mail-ww0-f53.google.com ([74.125.82.53]:48745) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RkKRK-0006pk-CE for qemu-devel@nongnu.org; Mon, 09 Jan 2012 13:59:26 -0500 Received: by wgbdt10 with SMTP id dt10so1910785wgb.10 for ; Mon, 09 Jan 2012 10:59:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=from:to:subject:date:message-id:x-mailer:in-reply-to:references; bh=WzAqH59Aap643YT1VjVmV+xm4yithISAvkaM7XZFz3Y=; b=AcM4EG1/u/IAy3PZ2hImwzmpkt4F2mkp0ig6qpGeP+1B0lgqGhWm6NDcv13qBDyzf/ dkumHubX+irThaZbsB32XanUUGfMug08/NdzLFjtITiTxPb6HBJeqHeGdnTr3kb4wIIq Dl6KoOsiRTxbgu2XxTecU6YAafW/JpS0Fc/gI= Received: by 10.180.72.162 with SMTP id e2mr6820047wiv.8.1326135565380; Mon, 09 Jan 2012 10:59:25 -0800 (PST) Received: from azaborow-mobl.ger.corp.intel.com (80.224.83.140.static.user.ono.com. [80.224.83.140]) by mx.google.com with ESMTPS id d9sm7040259wiy.2.2012.01.09.10.59.23 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 09 Jan 2012 10:59:24 -0800 (PST) Received: by azaborow-mobl.ger.corp.intel.com (sSMTP sendmail emulation); Mon, 09 Jan 2012 09:00:50 +0100 From: Andrzej Zaborowski To: qemu-devel@nongnu.org Date: Mon, 9 Jan 2012 08:57:55 +0100 Message-Id: <1326095876-31319-6-git-send-email-balrogg@gmail.com> X-Mailer: git-send-email 1.7.1.86.g0e460.dirty In-Reply-To: <1326095876-31319-1-git-send-email-balrogg@gmail.com> References: <1326095876-31319-1-git-send-email-balrogg@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 74.125.82.53 Subject: [Qemu-devel] [PATCH 5/6] gl: virtio-serial port driver for OpenGL passthrough. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- Makefile.target | 3 +- hw/virtio-gl-port.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+), 1 deletions(-) create mode 100644 hw/virtio-gl-port.c diff --git a/Makefile.target b/Makefile.target index 21e9927..3d52c71 100644 --- a/Makefile.target +++ b/Makefile.target @@ -190,7 +190,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..c75f7aa --- /dev/null +++ b/hw/virtio-gl-port.c @@ -0,0 +1,222 @@ +/* + * 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 . + */ + +#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 = NULL; +static size_t cmd_buffer_bytes = 0; +static size_t cmd_buffer_size = 0; +static const uint8_t *ret_buffer = NULL; +static size_t ret_buffer_bytes = 0; + +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) +{ + VirtIOSerialPortInfo *info = + DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + static int exists = 0; + + 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; + } + + info->have_data = virtio_gl_have_data; + info->guest_ready = virtio_gl_guest_ready; + info->guest_close = virtio_gl_guest_close; + + /* 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) +{ + VirtIOSerialPortInfo *info = + DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + + virtio_serial_close(port); + + info->have_data = NULL; + info->guest_ready = NULL; + info->guest_close = NULL; + + if (cmd_buffer) { + g_free(cmd_buffer); + cmd_buffer = NULL; + cmd_buffer_size = 0; + cmd_buffer_bytes = 0; + } + + return 0; +} + +static VirtIOSerialPortInfo virtio_gl_port_info = { + .qdev.name = "virtio-gl-port", + .qdev.size = sizeof(VirtIOSerialPort), + .init = virtio_gl_port_initfn, + .exit = virtio_gl_port_exitfn, +}; + +static void virtio_gl_port_register(void) +{ + virtio_serial_port_qdev_register(&virtio_gl_port_info); +} +device_init(virtio_gl_port_register)