From e80f89aa1daa34c0794cfe383d356fb6cea7c893 Mon Sep 17 00:00:00 2001 From: "dk77.yun" Date: Wed, 14 Sep 2011 20:10:57 +0900 Subject: [PATCH] [Title] add skeleton code for virtio example [Type] [Module] [Priority] [CQ#] [Redmine#] [Problem] [Cause] [Solution] [TestCase] --- arch/x86/configs/i386_emul_defconfig | 1 + drivers/virtio/Kconfig | 5 + drivers/virtio/Makefile | 1 + drivers/virtio/virtio_example.c | 350 +++++++++++++++++++++++++++ 4 files changed, 357 insertions(+) create mode 100644 drivers/virtio/virtio_example.c diff --git a/arch/x86/configs/i386_emul_defconfig b/arch/x86/configs/i386_emul_defconfig index edffcde1750e..8893c67ab156 100644 --- a/arch/x86/configs/i386_emul_defconfig +++ b/arch/x86/configs/i386_emul_defconfig @@ -2572,6 +2572,7 @@ CONFIG_VIRTUALIZATION=y # CONFIG_LGUEST is not set CONFIG_VIRTIO=y CONFIG_VIRTIO_RING=y +# CONFIG_VIRTIO_EXAMPLE=y CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_BALLOON=y CONFIG_BINARY_PRINTF=y diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 3dd6294d10b6..8d088dd8b4c5 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -7,6 +7,11 @@ config VIRTIO_RING tristate depends on VIRTIO +config VIRTIO_EXAMPLE + tristate "Virtio example driver (EXPERIMENTAL)" + select VIRTIO + select VIRTIO_RING + config VIRTIO_PCI tristate "PCI driver for virtio devices (EXPERIMENTAL)" depends on PCI && EXPERIMENTAL diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 6738c446c199..f7955ce8721c 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_VIRTIO) += virtio.o obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o +obj-$(CONFIG_VIRTIO_EXAMPLE) += virtio_example.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o diff --git a/drivers/virtio/virtio_example.c b/drivers/virtio/virtio_example.c new file mode 100644 index 000000000000..735ec84d0e52 --- /dev/null +++ b/drivers/virtio/virtio_example.c @@ -0,0 +1,350 @@ +/* + * 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; version 2 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, 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 + +// include/linux/virtio_ids.h +#define VIRTIO_ID_EXAMPLE 10 /* virtio example */ +// include/linux/virtio_balloon.h +#define VIRTIO_EXAMPLE_F_MUST_TELL_HOST 0 + +#define DEVICE_NAME "virtexample" + +/* Define to use debugging checksums on transfers */ +#undef DEBUG_EXIO + +/* Enable debug messages. */ +#define VIRTIO_EX_DEBUG + +#if defined(VIRTIO_EX_DEBUG) +#define logout(fmt, ...) \ + printk(KERN_INFO "[virtio][%s][%d]" fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define logout(fmt, ...) ((void)0) +#endif + +struct virtio_ex_data { + char *buffer; + int pages; + unsigned int pid; +}; + +struct virtio_ex_header { + int pid; + int buf_size; + int r_buf_size; +#ifdef DEBUG_EXIO + int sum; +#endif + char buffer; +} __packed; + +#define to_virtio_ex_data(a) ((struct virtio_ex_data *)(a)->private_data) + +#ifdef DEBUG_EXIO +#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; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_EXAMPLE, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +/* 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; + + logout("\n"); + /* 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_ex_data *exdata) +{ + struct scatterlist *sg, *sg_list; + unsigned int count, ret, o_page, i_page, sg_entries; + struct virtio_ex_header *header = + (struct virtio_ex_header *)exdata->buffer; + + logout("\n"); + 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 = exdata->pid; + + if ((o_page && i_page) && + (o_page > exdata->pages || i_page > exdata->pages)) { + i_page = 0; + } + + if (o_page > exdata->pages) + o_page = exdata->pages; + + if (i_page > exdata->pages) + i_page = exdata->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, exdata->buffer, o_page); + sg = vmalloc_to_sg(sg, exdata->buffer, i_page); + + if (!sg) { + ret = -EIO; + goto out_free; + } + + /* Transfer data */ + if (vq->vq_ops->add_buf(vq, sg_list, o_page, i_page, (void *)1) >= 0) { + vq->vq_ops->kick(vq); + /* Chill out until it's done with the buffer. */ + while (!vq->vq_ops->get_buf(vq, &count)) + cpu_relax(); + } + +out_free: + kfree(sg_list); +out: + return ret; +} + +static void free_buffer(struct virtio_ex_data *exdata) +{ + logout("\n"); + if (exdata->buffer) { + vfree(exdata->buffer); + exdata->buffer = NULL; + } +} + +static int virtexample_open(struct inode *inode, struct file *file) +{ + struct virtio_ex_data *exdata = NULL; + + logout("\n"); + + exdata = kzalloc(sizeof(struct virtio_ex_data), GFP_KERNEL); + if (!exdata) + return -ENXIO; + + exdata->pid = pid_nr(task_pid(current)); + + file->private_data = exdata; + + return 0; +} + +static int virtexample_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct virtio_ex_data *exdata = to_virtio_ex_data(filp); + int pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE; + + logout("\n"); + + /* Set a reasonable limit */ + if (pages > 16) + return -ENOMEM; + + /* for now, just allow one buffer to be mmap()ed. */ + if (exdata->buffer) + return -EIO; + + exdata->buffer = vmalloc_user(pages*PAGE_SIZE); + + if (!exdata->buffer) + return -ENOMEM; + + exdata->pages = pages; + + if (remap_vmalloc_range(vma, exdata->buffer, 0) < 0) { + vfree(exdata->buffer); + return -EIO; + } + + vma->vm_flags |= VM_DONTEXPAND; + + return 0; +} + +static int virtexample_fsync(struct file *filp, int datasync) +{ + struct virtio_ex_data *exdata = to_virtio_ex_data(filp); + + logout("\n"); + put_data(exdata); + + return 0; +} + +static int virtexample_release(struct inode *inode, struct file *file) +{ + struct virtio_ex_data *exdata = to_virtio_ex_data(file); + + logout("\n"); + if (exdata && exdata->buffer) { + struct virtio_ex_header *header = + (struct virtio_ex_header *)exdata->buffer; + + /* Make sure the host hears about the process ending / dying */ + header->pid = exdata->pid; + header->buf_size = SIZE_OUT_HEADER + 2; + header->r_buf_size = SIZE_IN_HEADER; + *(short *)(&header->buffer) = -1; + + put_data(exdata); + free_buffer(exdata); + } + + kfree(exdata); + + return 0; +} + +static const struct file_operations virtexample_fops = { + .owner = THIS_MODULE, + .open = virtexample_open, + .mmap = virtexample_mmap, + .fsync = virtexample_fsync, + .release = virtexample_release, +}; + +static struct miscdevice virtexample_dev = { + MISC_DYNAMIC_MINOR, + DEVICE_NAME, + &virtexample_fops +}; + +static int virtexample_probe(struct virtio_device *vdev) +{ + int ret; + + logout("\n"); + + /* We expect a single virtqueue. */ + vq = virtio_find_single_vq(vdev, NULL, "output"); + if (IS_ERR(vq)) + return PTR_ERR(vq); + + ret = misc_register(&virtexample_dev); + if (ret) { + printk(KERN_ERR "virtexample: cannot register virtexample_dev as misc"); + return -ENODEV; + } + + return 0; +} + +static void __devexit virtexample_remove(struct virtio_device *vdev) +{ + logout("\n"); + vdev->config->reset(vdev); + misc_deregister(&virtexample_dev); + vdev->config->del_vqs(vdev); +} + +static void virtexample_changed(struct virtio_device *vdev) +{ + logout("\n"); + //struct virtio_balloon *vb = vdev->priv; + //wake_up(&vb->config_change); +} + + +static unsigned int features[] = { VIRTIO_EXAMPLE_F_MUST_TELL_HOST }; + +static struct virtio_driver virtio_example = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtexample_probe, + .remove = __devexit_p(virtexample_remove), + .config_changed = virtexample_changed, +}; + +static int __init init(void) +{ + logout("\n"); + return register_virtio_driver(&virtio_example); +} + +static void __exit fini(void) +{ + logout("\n"); + unregister_virtio_driver(&virtio_example); +} + +module_init(init); +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio example driver"); +MODULE_LICENSE("GPL v2"); + -- 2.34.1