M/ 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,315 @@ +/* + * Copyright (C) 2010 Intel Corporation + * + * Author: Ian Molton + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ + struct scatterlist *sgs[2]; + sgs[0] = &sg_list[0]; + sgs[1] = &sg_list[1]; + if (virtqueue_add_sgs(vq, sgs, o_page, i_page, (void *)1, GFP_ATOMIC) >= 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 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 = 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 --- linux-3.4/include/uapi/linux/virtio_ids.h~ 2012-05-21 08:42:02.000000000 +0200 +++ linux-3.4/include/uapi/linux/virtio_ids.h 2012-05-21 09:02:40.065957644 +0200 @@ -34,6 +34,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_RPMSG 7 /* virtio remote processor messaging */ #define VIRTIO_ID_SCSI 8 /* virtio scsi */ #define VIRTIO_ID_9P 9 /* 9p virtio console */