[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