[MeeGo-dev] [PATCH 1/1] virtio-gl kernel module for GL acceleration of Emulator (on behalf of Ian Molton)

Lv, Zhiyuan zhiyuan.lv at intel.com
Tue Aug 10 02:39:13 PDT 2010


Ian is on travel and I am sending this patch on behalf of him.

The patch is to add a kernel device driver of virtio-gl, which is used for data communication between QEMU emulator host and client OS running in it. This virtual device is used for GL acceleration in QEMU.

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 7cfcc62..c074b73 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1113,6 +1113,14 @@ config TELCLOCK
          /sys/devices/platform/telco_clock, with a number of files for
          controlling the behavior of this hardware.

+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)
+
 config DEVPORT
        bool
        depends on !M68K
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 88d6eac..f77e9d6 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -99,6 +99,8 @@ obj-$(CONFIG_CS5535_GPIO)     += cs5535_gpio.o
 obj-$(CONFIG_GPIO_TB0219)      += tb0219.o
 obj-$(CONFIG_TELCLOCK)         += tlclk.o

+obj-$(CONFIG_VIRTIOGL)         += virtio-gl.o
+
 obj-$(CONFIG_MWAVE)            += mwave/
 obj-$(CONFIG_AGP)              += agp/
 obj-$(CONFIG_PCMCIA)           += pcmcia/
diff --git a/drivers/char/virtio-gl.c b/drivers/char/virtio-gl.c
new file mode 100644
index 0000000..26bf5ae
--- /dev/null
+++ b/drivers/char/virtio-gl.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2010 Intel Corporation
+ *
+ * Author: Ian Molton <ian.molton at 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/io.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/virtio.h>
+#include <linux/virtio_rng.h>
+
+//#include <asm/uaccess.h>     /* for put_user */
+//#include <asm/current.h>
+
+struct virtio_gl_data
+{
+       char *buffer;
+       int pages;
+       unsigned int pid;
+};
+
+#define SIZE_OUT_HEADER (4*3)
+#define SIZE_IN_HEADER 4
+
+#define to_virtio_gl_data(a)   ((struct virtio_gl_data *)(a)->private_data)
+
+int init_module(void);
+void cleanup_module(void);
+static int device_open(struct inode *, struct file *);
+static int device_release(struct inode *, struct file *);
+static int device_mmap(struct file *, struct vm_area_struct *);
+static int device_fsync (struct file *, struct dentry *, int datasync);
+
+#define DEVICE_NAME "glmem"    /* Dev name as it appears in /proc/devices   */
+
+static struct virtqueue *vq;
+
+/*
+ * Global variables are declared as static, so are global within the file.
+ */
+
+static int major;              /* Major number assigned to our device driver */
+
+static struct file_operations fops = {
+       .open = device_open,
+       .mmap = device_mmap,
+       .fsync = device_fsync,
+       .release = device_release,
+};
+
+/* This is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c with
+ * some modifications
+ */
+static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int length, int r_length, int *sg_out_elem, int *sg_in_elem)
+{
+       unsigned char *virt_o = virt;
+       struct scatterlist *sglist;
+       struct page *pg;
+       int nr_out_pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
+       int nr_in_pages = (r_length+PAGE_SIZE-1)/PAGE_SIZE;
+       int sg_entries;
+       int i;
+
+       /* unaligned */
+       BUG_ON ((ulong)virt & ~PAGE_MASK);
+
+       sg_entries = nr_out_pages + nr_in_pages;
+
+       sglist = kcalloc(sg_entries, sizeof(struct scatterlist), GFP_KERNEL);
+       if (!sglist) {
+               return NULL;
+        }
+       sg_init_table(sglist, sg_entries);
+
+       /* Fill with elements for the data */
+       for (i = 0; i < nr_out_pages; i++) {
+               pg = vmalloc_to_page(virt);
+               if (!pg)
+                       goto err;
+
+               sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+               virt += PAGE_SIZE;
+       }
+
+       virt = virt_o;
+
+       for (; i < nr_out_pages + nr_in_pages; i++) {
+                pg = vmalloc_to_page(virt);
+                if (!pg)
+                        goto err;
+                sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+                virt += PAGE_SIZE;
+        }
+
+       *sg_out_elem = nr_out_pages;
+       *sg_in_elem = nr_in_pages;
+       return sglist;
+
+ err:
+       kfree(sglist);
+       return NULL;
+}
+
+static char *virtiogl_devnode(struct device *dev, mode_t *mode)
+{
+       *mode = 0666;
+        return NULL;
+}
+
+
+static struct class *virtiogl_class;
+
+static int virtmem_probe(struct virtio_device *vdev)
+{
+        /* We expect a single virtqueue. */
+        vq = virtio_find_single_vq(vdev, NULL, "output");
+        if (IS_ERR(vq))
+                return PTR_ERR(vq);
+
+        major = register_chrdev(0, DEVICE_NAME, &fops);
+
+        if (major < 0) {
+          printk(KERN_ALERT "Registering char device failed with %d\n", major);
+          return major;
+        }
+
+       virtiogl_class = class_create(THIS_MODULE, "virtio_gl");
+       if (IS_ERR(virtiogl_class))
+               return PTR_ERR(virtiogl_class);
+
+       virtiogl_class->devnode = virtiogl_devnode;
+       device_create(virtiogl_class, NULL, MKDEV(major, 0),
+                              NULL, DEVICE_NAME);
+
+        return 0;
+}
+
+#define MAX(a, b) ((a)<(b)?(b):(a))
+
+static int put_data(struct virtio_gl_data *gldata)
+{
+       struct scatterlist *sg;
+        unsigned int count;
+       int len, rlen;
+       int sg_out_elem, sg_in_elem;
+
+       len = ((int*)(gldata->buffer))[1];
+       rlen = ((int*)(gldata->buffer))[2];
+       ((int*)gldata->buffer)[0] = gldata->pid;
+
+       // Ensure at least enough space is reserved for return status
+       if(!rlen)
+               rlen = 4;
+
+        BUG_ON(gldata->pages*PAGE_SIZE < MAX(len,rlen));
+
+       sg = vmalloc_to_sg(gldata->buffer, len, rlen, &sg_out_elem, &sg_in_elem);
+        /* add_buf wants a token to identify this buffer: we hand it any
+         * non-NULL pointer, since there's only ever one buffer. */
+        if (virtqueue_add_buf(vq, sg, sg_out_elem, sg_in_elem, (void *)1) >= 0) {
+                /* Tell Host to go! */
+                virtqueue_kick(vq);
+                /* Chill out until it's done with the buffer. */
+                while (!virtqueue_get_buf(vq, &count))
+                        cpu_relax();
+        }
+
+       kfree(sg);
+
+        /* We're expected to return the amount of data we wrote: all of it. */
+        return len;
+}
+
+static void __devexit virtmem_remove(struct virtio_device *vdev)
+{
+        vdev->config->reset(vdev);
+        /*
+         * Unregister the device
+         */
+        unregister_chrdev(major, DEVICE_NAME);
+
+        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_mem_driver = {
+        .driver.name =  KBUILD_MODNAME,
+        .driver.owner = THIS_MODULE,
+        .id_table =     id_table,
+        .probe =        virtmem_probe,
+        .remove =       __devexit_p(virtmem_remove),
+};
+
+/*
+ * This function is called when the module is loaded
+ */
+int init_module(void)
+{
+       return register_virtio_driver(&virtio_mem_driver);
+}
+
+/*
+ * This function is called when the module is unloaded
+ */
+void cleanup_module(void)
+{
+       unregister_virtio_driver(&virtio_mem_driver);
+}
+
+/*
+ * Methods
+ */
+
+/*
+ * Called when a process tries to open the device file, like
+ * "cat /dev/mycharfile"
+ */
+
+static int device_open(struct inode *inode, struct file *file)
+{
+       struct virtio_gl_data *gldata = kzalloc(sizeof(struct virtio_gl_data), GFP_KERNEL);
+
+       gldata->pid = pid_nr(task_pid(current));
+
+       file->private_data = gldata;
+
+       try_module_get(THIS_MODULE);
+
+       return 0;
+}
+
+static void free_buffer(struct virtio_gl_data *gldata) {
+       int i;
+
+        if(gldata->buffer) {
+                for (i = 0; i < gldata->pages * PAGE_SIZE; i+= PAGE_SIZE)
+                        ClearPageReserved(vmalloc_to_page((gldata->buffer + i)));
+                vfree(gldata->buffer);
+                gldata->buffer = NULL;
+        }
+}
+
+/*
+ * Called when a process closes the device file.
+ */
+static int device_release(struct inode *inode, struct file *file)
+{
+       struct virtio_gl_data *gldata = to_virtio_gl_data(file);
+
+       if(gldata && gldata->buffer) {
+               /* Make sure the host hears about the process ending / dying */
+               ((int*)gldata->buffer)[0] = gldata->pid;
+               ((int*)gldata->buffer)[1] = SIZE_OUT_HEADER + 2;
+               ((int*)gldata->buffer)[2] = SIZE_IN_HEADER;
+               *(short*)(gldata->buffer + SIZE_OUT_HEADER) = -1; // Death!
+
+               put_data(gldata);
+
+               free_buffer(gldata);
+       }
+
+       kfree(gldata);
+
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+static int device_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct virtio_gl_data *gldata = to_virtio_gl_data(filp);
+       int i;
+       int length = vma->vm_end - vma->vm_start;
+       int pages = length / PAGE_SIZE;
+
+       // Hack - for now, just allow one buffer.
+       if(gldata->buffer)
+               free_buffer(gldata);
+
+       gldata->buffer = vmalloc(pages*PAGE_SIZE);
+        memset(gldata->buffer, 0, pages*PAGE_SIZE);
+       gldata->pages = pages;
+
+        for (i = 0; i < pages * PAGE_SIZE; i+= PAGE_SIZE) {
+                SetPageReserved(vmalloc_to_page((gldata->buffer + i)));
+               if (remap_pfn_range(vma,
+                                   (vma->vm_start + i),
+                                   vmalloc_to_pfn((gldata->buffer + i)),
+                                   PAGE_SIZE,
+                                   PAGE_SHARED) < 0) {
+                       return -EIO;
+               }
+
+        }
+
+       return 0;
+}
+
+static int device_fsync (struct file *filp, struct dentry *dirent, int datasync)
+{
+       struct virtio_gl_data *gldata = to_virtio_gl_data(filp);
+
+       put_data(gldata);
+
+       return 0;
+}
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio gl passthrough driver");
+MODULE_LICENSE("GPL");
+
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 */

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.meego.com/pipermail/meego-dev/attachments/20100810/2fb48404/attachment.html>


More information about the MeeGo-dev mailing list