lkml.org 
[lkml]   [2010]   [Oct]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] Implement a virtio GPU transport
This patch implements a virtio-based transport for use by a virtualised 
OpenGL passthrough implementation.

The libGL and qemu-gl code to support this patch are available here:

http://gitorious.org/vm-gl-accel/qemu-gl
http://gitorious.org/vm-gl-accel/qemu-libgl


Comments please!
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index 30879df..35699a0 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -1 +1 @@
-obj-y += drm/ vga/
+obj-y += drm/ vga/ misc/
diff --git a/drivers/gpu/misc/Kconfig b/drivers/gpu/misc/Kconfig
new file mode 100644
index 0000000..50043d3
--- /dev/null
+++ b/drivers/gpu/misc/Kconfig
@@ -0,0 +1,8 @@
+config VIRTIOGL
+ tristate "Virtio userspace memory transport"
+ depends on VIRTIO_PCI
+ default n
+ help
+ A Driver to facilitate transferring data from userspace to a
+ hypervisor (eg. qemu)
+
diff --git a/drivers/gpu/misc/Makefile b/drivers/gpu/misc/Makefile
new file mode 100644
index 0000000..d9ab333
--- /dev/null
+++ b/drivers/gpu/misc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIRTIOGL) += virtio-gl.o
diff --git a/drivers/gpu/misc/virtio-gl.c b/drivers/gpu/misc/virtio-gl.c
new file mode 100644
index 0000000..8882bda
--- /dev/null
+++ b/drivers/gpu/misc/virtio-gl.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2010 Intel Corporation
+ *
+ * Author: Ian Molton <ian.molton@collabora.co.uk>
+ *
+ * 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 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+
+#define DEVICE_NAME "glmem"
+
+/* Define to use debugging checksums on transfers */
+#undef DEBUG_GLIO
+
+struct virtio_gl_data {
+ char *buffer;
+ int pages;
+ unsigned int pid;
+};
+
+struct virtio_gl_header {
+ int pid;
+ int buf_size;
+ int r_buf_size;
+#ifdef DEBUG_GLIO
+ int sum;
+#endif
+ char buffer;
+} __packed;
+
+#define to_virtio_gl_data(a) ((struct virtio_gl_data *)(a)->private_data)
+
+#ifdef DEBUG_GLIO
+#define SIZE_OUT_HEADER (sizeof(int)*4)
+#define SIZE_IN_HEADER (sizeof(int)*2)
+#else
+#define SIZE_OUT_HEADER (sizeof(int)*3)
+#define SIZE_IN_HEADER sizeof(int)
+#endif
+
+static struct virtqueue *vq;
+
+
+/* This is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c with
+ * some modifications
+ */
+static struct scatterlist *vmalloc_to_sg(struct scatterlist *sg_list,
+ unsigned char *virt, unsigned int pages)
+{
+ struct page *pg;
+
+ /* unaligned */
+ BUG_ON((ulong)virt & ~PAGE_MASK);
+
+ /* Fill with elements for the data */
+ while (pages) {
+ pg = vmalloc_to_page(virt);
+ if (!pg)
+ goto err;
+
+ sg_set_page(sg_list, pg, PAGE_SIZE, 0);
+ virt += PAGE_SIZE;
+ sg_list++;
+ pages--;
+ }
+
+ return sg_list;
+
+err:
+ kfree(sg_list);
+ return NULL;
+}
+
+static int put_data(struct virtio_gl_data *gldata)
+{
+ struct scatterlist *sg, *sg_list;
+ unsigned int count, ret, o_page, i_page, sg_entries;
+ struct virtio_gl_header *header =
+ (struct virtio_gl_header *)gldata->buffer;
+
+ ret = header->buf_size;
+
+ o_page = (header->buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
+ i_page = (header->r_buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
+
+ header->pid = gldata->pid;
+
+ if ((o_page && i_page) &&
+ (o_page > gldata->pages || i_page > gldata->pages)) {
+ i_page = 0;
+ }
+
+ if (o_page > gldata->pages)
+ o_page = gldata->pages;
+
+ if (i_page > gldata->pages)
+ i_page = gldata->pages;
+
+ if (!o_page)
+ o_page = 1;
+
+ sg_entries = o_page + i_page;
+
+ sg_list = kcalloc(sg_entries, sizeof(struct scatterlist), GFP_KERNEL);
+
+ if (!sg_list) {
+ ret = -EIO;
+ goto out;
+ }
+
+ sg_init_table(sg_list, sg_entries);
+
+ sg = vmalloc_to_sg(sg_list, gldata->buffer, o_page);
+ sg = vmalloc_to_sg(sg, gldata->buffer, i_page);
+
+ if (!sg) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ /* Transfer data */
+ if (virtqueue_add_buf(vq, sg_list, o_page, i_page, (void *)1) >= 0) {
+ virtqueue_kick(vq);
+ /* Chill out until it's done with the buffer. */
+ while (!virtqueue_get_buf(vq, &count))
+ cpu_relax();
+ }
+
+out_free:
+ kfree(sg_list);
+out:
+ return ret;
+}
+
+static void free_buffer(struct virtio_gl_data *gldata)
+{
+ if (gldata->buffer) {
+ vfree(gldata->buffer);
+ gldata->buffer = NULL;
+ }
+}
+
+static int glmem_open(struct inode *inode, struct file *file)
+{
+ struct virtio_gl_data *gldata = kzalloc(sizeof(struct virtio_gl_data),
+ GFP_KERNEL);
+
+ if (!gldata)
+ return -ENXIO;
+
+ gldata->pid = pid_nr(task_pid(current));
+
+ file->private_data = gldata;
+
+ return 0;
+}
+
+static int glmem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct virtio_gl_data *gldata = to_virtio_gl_data(filp);
+ int pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE;
+
+ /* Set a reasonable limit */
+ if (pages > 16)
+ return -ENOMEM;
+
+ /* for now, just allow one buffer to be mmap()ed. */
+ if (gldata->buffer)
+ return -EIO;
+
+ gldata->buffer = vmalloc_user(pages*PAGE_SIZE);
+
+ if (!gldata->buffer)
+ return -ENOMEM;
+
+ gldata->pages = pages;
+
+ if (remap_vmalloc_range(vma, gldata->buffer, 0) < 0) {
+ vfree(gldata->buffer);
+ return -EIO;
+ }
+
+ vma->vm_flags |= VM_DONTEXPAND;
+
+ return 0;
+}
+
+static int glmem_fsync(struct file *filp, int datasync)
+{
+ struct virtio_gl_data *gldata = to_virtio_gl_data(filp);
+
+ put_data(gldata);
+
+ return 0;
+}
+
+static int glmem_release(struct inode *inode, struct file *file)
+{
+ struct virtio_gl_data *gldata = to_virtio_gl_data(file);
+
+ if (gldata && gldata->buffer) {
+ struct virtio_gl_header *header =
+ (struct virtio_gl_header *)gldata->buffer;
+
+ /* Make sure the host hears about the process ending / dying */
+ header->pid = gldata->pid;
+ header->buf_size = SIZE_OUT_HEADER + 2;
+ header->r_buf_size = SIZE_IN_HEADER;
+ *(short *)(&header->buffer) = -1;
+
+ put_data(gldata);
+ free_buffer(gldata);
+ }
+
+ kfree(gldata);
+
+ return 0;
+}
+
+static const struct file_operations glmem_fops = {
+ .owner = THIS_MODULE,
+ .open = glmem_open,
+ .mmap = glmem_mmap,
+ .fsync = glmem_fsync,
+ .release = glmem_release,
+};
+
+static struct miscdevice glmem_dev = {
+ MISC_DYNAMIC_MINOR,
+ DEVICE_NAME,
+ &glmem_fops
+};
+
+static int glmem_probe(struct virtio_device *vdev)
+{
+ int ret;
+
+ /* We expect a single virtqueue. */
+ vq = virtio_find_single_vq(vdev, NULL, "output");
+ if (IS_ERR(vq))
+ return PTR_ERR(vq);
+
+ ret = misc_register(&glmem_dev);
+ if (ret) {
+ printk(KERN_ERR "glmem: cannot register glmem_dev as misc");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __devexit glmem_remove(struct virtio_device *vdev)
+{
+ vdev->config->reset(vdev);
+ misc_deregister(&glmem_dev);
+ vdev->config->del_vqs(vdev);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_GL, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver virtio_gl_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = id_table,
+ .probe = glmem_probe,
+ .remove = __devexit_p(glmem_remove),
+};
+
+static int __init glmem_init(void)
+{
+ return register_virtio_driver(&virtio_gl_driver);
+}
+
+static void __exit glmem_exit(void)
+{
+ unregister_virtio_driver(&virtio_gl_driver);
+}
+
+module_init(glmem_init);
+module_exit(glmem_exit);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio gl passthrough driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a14..9a9a6cc 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -16,6 +16,7 @@ source "drivers/char/agp/Kconfig"
source "drivers/gpu/vga/Kconfig"

source "drivers/gpu/drm/Kconfig"
+source "drivers/gpu/misc/Kconfig"

config VGASTATE
tristate
diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h
index 06660c0..663b496 100644
--- a/include/linux/virtio_ids.h
+++ b/include/linux/virtio_ids.h
@@ -12,6 +12,7 @@
#define VIRTIO_ID_CONSOLE 3 /* virtio console */
#define VIRTIO_ID_RNG 4 /* virtio ring */
#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
+#define VIRTIO_ID_GL 6 /* virtio usermem */
#define VIRTIO_ID_9P 9 /* 9p virtio console */

#endif /* _LINUX_VIRTIO_IDS_H */
\
 
 \ /
  Last update: 2010-10-06 18:07    [from the cache]
©2003-2011 Jasper Spaans