[Title] Add general purpose interface (virtio)
authordongkyun.yun <dk77.yun@samsung.com>
Fri, 25 Nov 2011 11:02:26 +0000 (20:02 +0900)
committerdongkyun.yun <dk77.yun@samsung.com>
Fri, 25 Nov 2011 11:02:26 +0000 (20:02 +0900)
[Type]      Enhancement
[Module]    // Module Name - (Main / Sub)
[Priority]  // Importance : Critical / Major / Minor
[CQ#]       // CQ Issue Number
[Redmine#]  // Redmine Isuue Number
[Problem]   // Problem Description
[Cause]     // Cause Description
[Solution]  // Solution Description
[TestCase]  // Executed the test-target (How to)

arch/x86/configs/i386_emul_defconfig
drivers/virtio/Kconfig
drivers/virtio/Makefile
drivers/virtio/virtio_example.c [deleted file]
drivers/virtio/virtio_gpi.c [new file with mode: 0644]
include/linux/virtio_ids.h

index 1b0b5cc77c4eab76d31203abe35f384ac8a18fb6..8bd30019ebf474b15910e1c44ca32aba3c49df26 100644 (file)
@@ -2576,7 +2576,7 @@ CONFIG_VIRTUALIZATION=y
 # CONFIG_LGUEST is not set
 CONFIG_VIRTIO=y
 CONFIG_VIRTIO_RING=y
-# CONFIG_VIRTIO_EXAMPLE=y
+CONFIG_VIRTIO_GPI=y
 CONFIG_VIRTIO_PCI=y
 CONFIG_VIRTIO_BALLOON=y
 CONFIG_BINARY_PRINTF=y
index 8d088dd8b4c5936b76b46b622ceca598d4a4eaed..8acc821cc219199913fa2cb18cdcd3b767403345 100644 (file)
@@ -7,8 +7,8 @@ config VIRTIO_RING
        tristate
        depends on VIRTIO
 
-config VIRTIO_EXAMPLE
-       tristate "Virtio example driver (EXPERIMENTAL)"
+config VIRTIO_GPI
+       tristate "Virtio general purpose driver"
        select VIRTIO
        select VIRTIO_RING
 
index f7955ce8721c6a5959c72d9cf2c78835a3f3f7e7..dd905f528e71e1a7e38cb41339bcbbc533aba82c 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
-obj-$(CONFIG_VIRTIO_EXAMPLE) += virtio_example.o
+obj-$(CONFIG_VIRTIO_GPI) += virtio_gpi.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
deleted file mode 100644 (file)
index ff42932..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-/* Virtio example implementation
- *
- *  Copyright 2011 Dongkyun Yun
- *
- *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  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 <asm/uaccess.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 DEVICE_MINOR_NUM 70
-
-/* 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 {
-       int pid;
-
-       int w_buf_size;
-       char *w_buf;
-
-       int r_buf_size;
-       char *r_buf;
-}__packed;
-
-#define to_virtio_ex_data(a)   ((struct virtio_ex_data *)(a)->private_data)
-
-static struct virtqueue *vq = NULL;
-
-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;
-
-       /* 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);
-       sg_list = NULL;
-       return NULL;
-}
-
-static unsigned int put_data(struct virtio_ex_data *exdata)
-{
-       struct scatterlist *sg;
-       static struct scatterlist *sg_list = NULL;
-       unsigned int ret, count, i_page, o_page, sg_entries;
-
-       ret = exdata->w_buf_size;
-
-       o_page = (exdata->w_buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
-       i_page = (exdata->r_buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
-       if (!o_page)
-               o_page = 1;
-       if (!i_page)
-               i_page = 1;
-
-       logout("add_buf(out:%d, in:%d) \n", o_page*PAGE_SIZE, i_page*PAGE_SIZE);
-
-       sg_entries = o_page + i_page;
-
-       sg_list = kcalloc(sg_entries, sizeof(struct scatterlist), GFP_KERNEL);
-       if (!sg_list) {
-               logout("kcalloc is fail ");
-               ret = -EIO;
-               goto out;
-       }
-
-       sg_init_table(sg_list, sg_entries);
-
-       sg = vmalloc_to_sg(sg_list, exdata->w_buf, o_page);
-       sg = vmalloc_to_sg(sg, exdata->r_buf, i_page);
-       if (!sg) {
-               logout("vmalloc_to_sq is fail ");
-               ret = -EIO;
-               goto out;
-       }
-
-       /* 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:
-       if (sg_list){
-               kfree(sg_list);
-               sg_list = NULL;
-       }
-
-       return ret;
-}
-
-static void free_buffer(struct virtio_ex_data *exdata)
-{
-       if (exdata->w_buf) {
-               exdata->w_buf_size = 0;
-               vfree(exdata->w_buf);
-               exdata->w_buf= NULL;
-       }
-       if (exdata->r_buf) {
-               exdata->r_buf_size = 0;
-               vfree(exdata->r_buf);
-               exdata->r_buf= 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));
-       logout("pid %d \n", exdata->pid);
-
-       file->private_data = exdata;
-
-       return 0;
-}
-
-static int log_dump(char *buffer, int size)
-{
-       int i;
-       unsigned char *ptr = (unsigned char*)buffer;
-
-       logout("pid %d ", pid_nr(task_pid(current)));
-
-       logout("buffer[%p] size[%d] ", buffer, size);
-       logout("DATA BEGIN -------------- ");
-
-       for(i=0; i < size; i++){
-               logout(" %d =  %02x(%c) ", i, ptr[i], ptr[i]);
-       }
-       logout("DATA END  -------------- ");
-       return 0;
-}
-
-static ssize_t virtexample_write(struct file *filp, const char *buffer,
-               size_t count, loff_t * posp)
-{
-       struct virtio_ex_data *exdata = to_virtio_ex_data(filp);
-       ssize_t ret;
-
-       logout("\n");
-
-       /* for now, just allow one buffer to write(). */
-       if (exdata->w_buf)
-               return -EIO;
-
-       exdata->w_buf_size = count;
-       exdata->w_buf = vmalloc(exdata->w_buf_size);
-       if (!exdata->w_buf)
-               return -ENOMEM;
-
-       exdata->r_buf_size = count;
-       exdata->r_buf = vmalloc(exdata->r_buf_size);
-       if (!exdata->r_buf)
-               return -ENOMEM;
-
-       if (copy_from_user(exdata->w_buf, buffer, exdata->w_buf_size))
-               return -EINVAL;
-
-       log_dump(exdata->w_buf, exdata->w_buf_size);
-
-       put_data(exdata);
-
-       log_dump(exdata->r_buf, exdata->r_buf_size);
-
-       ret = exdata->w_buf_size;
-
-       return ret;
-}
-
-static int virtexample_release(struct inode *inode, struct file *file)
-{
-       struct virtio_ex_data *exdata = to_virtio_ex_data(file);
-
-       logout("\n");
-
-       if (exdata) {
-               free_buffer(exdata);
-       }
-
-       kfree(exdata);
-       return 0;
-}
-
-static const struct file_operations virtexample_fops = {
-       .owner          = THIS_MODULE,
-       .open           = virtexample_open,
-       .write      = virtexample_write,
-       .release        = virtexample_release,
-};
-
-static struct miscdevice virtexample_dev = {
-       DEVICE_MINOR_NUM,
-       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");
-
diff --git a/drivers/virtio/virtio_gpi.c b/drivers/virtio/virtio_gpi.c
new file mode 100644 (file)
index 0000000..7a44310
--- /dev/null
@@ -0,0 +1,334 @@
+/* Virtio general purpose interface implementation
+ *
+ *  Copyright 2011 Dongkyun Yun
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  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 <asm/uaccess.h>
+
+/* include/linux/virtio_balloon.h */
+#define VIRTIO_GPI_F_MUST_TELL_HOST 0
+
+#define DEVICE_NAME "virt_gpi"
+#define DEVICE_MINOR_NUM 70
+
+/* Define to use debugging checksums on transfers */
+#undef DEBUG_EXIO
+
+/* Enable debug messages. */
+#define VIRTIO_GPI_DEBUG
+
+#if defined(VIRTIO_GPI_DEBUG)
+#define logout(fmt, ...) \
+       printk(KERN_INFO "[virt_gpi][%s][%d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+struct virtio_gpi_data {
+       int pid;
+
+       int w_buf_size;
+       char *w_buf;
+
+       int r_buf_size;
+       char *r_buf;
+}__packed;
+
+#define to_virtio_gpi_data(a)   ((struct virtio_gpi_data *)(a)->private_data)
+
+static struct virtqueue *vq = NULL;
+
+static struct virtio_device_id id_table[] = {
+       { VIRTIO_ID_GPI, 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;
+
+       /* 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);
+       sg_list = NULL;
+       return NULL;
+}
+
+static unsigned int put_data(struct virtio_gpi_data *exdata)
+{
+       struct scatterlist *sg;
+       static struct scatterlist *sg_list = NULL;
+       unsigned int ret, count, i_page, o_page, sg_entries;
+
+       ret = exdata->w_buf_size;
+
+       o_page = (exdata->w_buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
+       i_page = (exdata->r_buf_size + PAGE_SIZE-1) >> PAGE_SHIFT;
+       if (!o_page)
+               o_page = 1;
+       if (!i_page)
+               i_page = 1;
+
+       logout("add_buf(out:%ld, in:%ld) \n", o_page*PAGE_SIZE, i_page*PAGE_SIZE);
+
+       sg_entries = o_page + i_page;
+
+       sg_list = kcalloc(sg_entries, sizeof(struct scatterlist), GFP_KERNEL);
+       if (!sg_list) {
+               logout("kcalloc is fail ");
+               ret = -EIO;
+               goto out;
+       }
+
+       sg_init_table(sg_list, sg_entries);
+
+       sg = vmalloc_to_sg(sg_list, exdata->w_buf, o_page);
+       sg = vmalloc_to_sg(sg, exdata->r_buf, i_page);
+       if (!sg) {
+               logout("vmalloc_to_sq is fail ");
+               ret = -EIO;
+               goto out;
+       }
+
+       /* 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:
+       if (sg_list){
+               kfree(sg_list);
+               sg_list = NULL;
+       }
+
+       return ret;
+}
+
+static void free_buffer(struct virtio_gpi_data *exdata)
+{
+       if (exdata->w_buf) {
+               exdata->w_buf_size = 0;
+               vfree(exdata->w_buf);
+               exdata->w_buf= NULL;
+       }
+       if (exdata->r_buf) {
+               exdata->r_buf_size = 0;
+               vfree(exdata->r_buf);
+               exdata->r_buf= NULL;
+       }
+} 
+static int virt_gpi_open(struct inode *inode, struct file *file)
+{
+       struct virtio_gpi_data *exdata = NULL;
+
+       logout("\n");
+
+       exdata = kzalloc(sizeof(struct virtio_gpi_data), GFP_KERNEL);
+       if (!exdata)
+               return -ENXIO;
+
+       exdata->pid = pid_nr(task_pid(current));
+       logout("pid %d \n", exdata->pid);
+
+       file->private_data = exdata;
+
+       return 0;
+}
+
+static int log_dump(char *buffer, int size)
+{
+       int i;
+       unsigned char *ptr = (unsigned char*)buffer;
+
+       logout("pid %d ", pid_nr(task_pid(current)));
+
+       logout("buffer[%p] size[%d] ", buffer, size);
+       logout("DATA BEGIN -------------- ");
+
+       for(i=0; i < size; i++){
+               logout(" %d =  %02x(%c) ", i, ptr[i], ptr[i]);
+       }
+       logout("DATA END  -------------- ");
+       return 0;
+}
+
+static ssize_t virt_gpi_write(struct file *filp, const char *buffer,
+               size_t count, loff_t * posp)
+{
+       struct virtio_gpi_data *exdata = to_virtio_gpi_data(filp);
+       ssize_t ret;
+
+       logout("\n");
+
+       /* for now, just allow one buffer to write(). */
+       if (exdata->w_buf)
+               return -EIO;
+
+       exdata->w_buf_size = count;
+       exdata->w_buf = vmalloc(exdata->w_buf_size);
+       if (!exdata->w_buf)
+               return -ENOMEM;
+
+       exdata->r_buf_size = count;
+       exdata->r_buf = vmalloc(exdata->r_buf_size);
+       if (!exdata->r_buf)
+               return -ENOMEM;
+
+       if (copy_from_user(exdata->w_buf, buffer, exdata->w_buf_size))
+               return -EINVAL;
+
+       logout("ping-pong: write data \n");
+       log_dump(exdata->w_buf, exdata->w_buf_size);
+
+       put_data(exdata);
+
+       logout("ping-pong: return data \n");
+       log_dump(exdata->r_buf, exdata->r_buf_size);
+
+       ret = exdata->w_buf_size;
+
+       return ret;
+}
+
+static int virt_gpi_release(struct inode *inode, struct file *file)
+{
+       struct virtio_gpi_data *exdata = to_virtio_gpi_data(file);
+
+       logout("\n");
+
+       if (exdata) {
+               free_buffer(exdata);
+       }
+
+       kfree(exdata);
+       return 0;
+}
+
+static const struct file_operations virt_gpi_fops = {
+       .owner          = THIS_MODULE,
+       .open           = virt_gpi_open,
+       .write      = virt_gpi_write,
+       .release        = virt_gpi_release,
+};
+
+static struct miscdevice virt_gpi_dev = {
+       DEVICE_MINOR_NUM,
+       DEVICE_NAME,
+       &virt_gpi_fops
+};
+
+static int virt_gpi_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(&virt_gpi_dev);
+       if (ret) {
+               printk(KERN_ERR "virt_gpi: cannot register virt_gpi_dev as misc");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __devexit virt_gpi_remove(struct virtio_device *vdev)
+{
+       logout("\n");
+       vdev->config->reset(vdev);
+       misc_deregister(&virt_gpi_dev);
+       vdev->config->del_vqs(vdev);
+}
+
+static void virt_gpi_changed(struct virtio_device *vdev)
+{
+       logout("\n");
+       //struct virtio_balloon *vb = vdev->priv;
+       //wake_up(&vb->config_change);
+}
+
+
+static unsigned int features[] = { VIRTIO_GPI_F_MUST_TELL_HOST };
+
+static struct virtio_driver virtio_gpi = {
+       .feature_table = features,
+       .feature_table_size = ARRAY_SIZE(features),
+       .driver.name =  KBUILD_MODNAME,
+       .driver.owner = THIS_MODULE,
+       .id_table =     id_table,
+       .probe =        virt_gpi_probe,
+       .remove =       __devexit_p(virt_gpi_remove),
+       .config_changed = virt_gpi_changed,
+};
+
+static int __init init(void)
+{
+       logout("\n");
+       return register_virtio_driver(&virtio_gpi);
+}
+
+static void __exit fini(void)
+{
+       logout("\n");
+       unregister_virtio_driver(&virtio_gpi);
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio general purpose driver");
+MODULE_LICENSE("GPL v2");
+
index 06660c0a78d720e5d4b2155041c10b3800c9ffbd..e588758f1aceb3d7d18a9ce5ccb3f28afe9105f3 100644 (file)
@@ -13,5 +13,6 @@
 #define VIRTIO_ID_RNG          4 /* virtio ring */
 #define VIRTIO_ID_BALLOON      5 /* virtio balloon */
 #define VIRTIO_ID_9P           9 /* 9p virtio console */
+#define VIRTIO_ID_GPI          20 /* virtio general purpose interface */
 
 #endif /* _LINUX_VIRTIO_IDS_H */