[Title] add skeleton code for virtio example
authordk77.yun <dk77.yun@samsung.com>
Wed, 14 Sep 2011 11:10:57 +0000 (20:10 +0900)
committerdk77.yun <dk77.yun@samsung.com>
Wed, 14 Sep 2011 11:10:57 +0000 (20:10 +0900)
[Type]
[Module]
[Priority]
[CQ#]
[Redmine#]
[Problem]
[Cause]
[Solution]
[TestCase]

arch/x86/configs/i386_emul_defconfig
drivers/virtio/Kconfig
drivers/virtio/Makefile
drivers/virtio/virtio_example.c [new file with mode: 0644]

index edffcde1750e50ea80ab35ef041e814743b7bf07..8893c67ab156b8c7360152dca393ce905d58c9f2 100644 (file)
@@ -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
index 3dd6294d10b64066b1660a33542ce2c726c2aa84..8d088dd8b4c5936b76b46b622ceca598d4a4eaed 100644 (file)
@@ -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
index 6738c446c199e5af4ebfdb655166c1d7dc2fa065..f7955ce8721c6a5959c72d9cf2c78835a3f3f7e7 100644 (file)
@@ -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 (file)
index 0000000..735ec84
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * 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; 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 <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>
+
+// 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");
+