[RFC-PATCH] Introducing virtio-example.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The main goal is to create an example to be used as template or
guideline for contributors when they wish to create a new virtio
device and to document "the right way" to do so.

It consists of several parts:

    1. The device specification
        * it can be found in the device header of the implementation
        * it will hopefully be added to the official virtio specification

    2. The device implementation for Qemu-KVM hypervisor
        * it can be found at https://github.com/ybettan/qemu/blob/virtio\
                /hw/virtio/virtio-example.c

    3. The device driver for linux
        * this patch content

    4. A blog on virtio
        * introducing the virtio concept
        * gives some motivation for virtio-devices to be used
        * bring extra documentation on "how to write":
            - device specification
            - device implementation
            - device driver for linux
        * it can be found at https://howtovms.wordpress.com

Signed-off-by: Yoni Bettan <ybettan@xxxxxxxxxx>
---
 samples/virtio/Makefile                |   1 +
 samples/virtio/virtio_example_driver.c | 239 +++++++++++++++++++++++++
 2 files changed, 240 insertions(+)
 create mode 100644 samples/virtio/Makefile
 create mode 100644 samples/virtio/virtio_example_driver.c

diff --git a/samples/virtio/Makefile b/samples/virtio/Makefile
new file mode 100644
index 000000000000..6f3cc811ca89
--- /dev/null
+++ b/samples/virtio/Makefile
@@ -0,0 +1 @@
+obj-m := virtio_example_driver.o
diff --git a/samples/virtio/virtio_example_driver.c b/samples/virtio/virtio_example_driver.c
new file mode 100644
index 000000000000..2f5a354009a1
--- /dev/null
+++ b/samples/virtio/virtio_example_driver.c
@@ -0,0 +1,239 @@
+/*
+ * Virtio example implementation.
+ *
+ *  Copyright 2019 Yoni Bettan Red Hat Corporation
+ *
+ *  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.
+ */
+
+#include <linux/virtio.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>               /* io map */
+#include <linux/dma-mapping.h>      /* DMA */
+#include <linux/kernel.h>           /* kstrtoint() func */
+#include <linux/virtio_config.h>    /* find_single_vq() func */
+
+
+#define VIRTIO_ID_EXAMPLE 21
+/* big enough to contain a string representing an integer */
+#define MAX_DATA_SIZE 20
+
+struct virtexample_info {
+	struct virtqueue *vq;
+    /*
+     * in - the data we get from the device
+     * out - the data we send to the device
+     */
+    int in, out;
+};
+
+
+
+//-----------------------------------------------------------------------------
+//                  sysfs - give user access to driver
+//-----------------------------------------------------------------------------
+
+static ssize_t
+virtio_buf_store(struct device *dev, struct device_attribute *attr,
+                    const char *buf, size_t count)
+{
+    char tmp_buf[MAX_DATA_SIZE];
+    int retval;
+	struct scatterlist sg_in, sg_out;
+	struct scatterlist *request[2];
+    /* cast dev into a virtio_device */
+    struct virtio_device *vdev = dev_to_virtio(dev);
+	struct virtexample_info *vi = vdev->priv;
+
+    /* copy the user buffer since it is a const buffer */
+    sprintf(tmp_buf, "%s", buf);
+
+    /* convert the data into an integer */
+    retval = kstrtoint(tmp_buf, 10, &vi->out);
+    if (retval) {
+        pr_alert("string converstion failed with error: %d\n", retval);
+    }
+
+    /* initialize a single entry sg lists, one for input and one for output */
+    sg_init_one(&sg_out, &vi->out, sizeof(int));
+    sg_init_one(&sg_in, &vi->in, sizeof(int));
+
+    /* build the request */
+    request[0] = &sg_out;
+    request[1] = &sg_in;
+
+	/* add the request to the queue, in_buf is sent as the buffer idetifier */
+    virtqueue_add_sgs(vi->vq, request, 1, 1, &vi->in, GFP_KERNEL);
+
+    /* notify the device */
+	virtqueue_kick(vi->vq);
+
+    return count;
+}
+
+static ssize_t
+virtio_buf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+    /* cast dev into a virtio_device */
+    struct virtio_device *vdev = dev_to_virtio(dev);
+	struct virtexample_info *vi = vdev->priv;
+
+    return sprintf(buf, "%d\n", vi->in);
+}
+
+/*
+ * struct device_attribute dev_attr_virtio_buf = {
+ *     .attr = {
+ *         .name = "virtio_buf",
+ *         .mode = 0644
+ *     },
+ *     .show = virtio_buf_show,
+ *     .store = virtio_buf_store
+ * }
+ */
+static DEVICE_ATTR_RW(virtio_buf);
+
+
+/*
+ * The example_attr defined above is then grouped in the struct attribute group
+ * as follows:
+ */
+struct attribute *example_attrs[] = {
+    &dev_attr_virtio_buf.attr,
+    NULL,
+};
+
+static const struct attribute_group example_attr_group = {
+    .name = "example", /* directory's name */
+    .attrs = example_attrs,
+};
+
+
+
+//-----------------------------------------------------------------------------
+//                              IRQ functions
+//-----------------------------------------------------------------------------
+
+static void example_irq_handler(struct virtqueue *vq)
+{
+
+	struct virtexample_info *vi = vq->vdev->priv;
+    unsigned int len;
+    int *res = NULL;
+
+    /* get the buffer from virtqueue */
+    res = virtqueue_get_buf(vi->vq, &len);
+
+    vi->in = *res;
+}
+
+
+//-----------------------------------------------------------------------------
+//                             driver functions
+//-----------------------------------------------------------------------------
+
+
+static int example_probe(struct virtio_device *vdev)
+{
+    int retval;
+    struct virtexample_info *vi = NULL;
+
+    /* create sysfiles for UI */
+    retval = sysfs_create_group(&vdev->dev.kobj, &example_attr_group);
+    if (retval) {
+        pr_alert("failed to create group in /sys/bus/virtio/devices/.../\n");
+    }
+
+    /* initialize driver data */
+	vi = kzalloc(sizeof(struct virtexample_info), GFP_KERNEL);
+	if (!vi)
+		return -ENOMEM;
+
+	/* We expect a single virtqueue. */
+	vi->vq = virtio_find_single_vq(vdev, example_irq_handler, "input");
+	if (IS_ERR(vi->vq)) {
+        pr_alert("failed to connect to the device virtqueue\n");
+	}
+
+    /* initialize the data to 0 */
+    vi->in = 0;
+    vi->out = 0;
+
+    /* store driver data inside the device to be accessed for all functions */
+    vdev->priv = vi;
+
+    return 0;
+}
+
+static void example_remove(struct virtio_device *vdev)
+{
+	struct virtexample_info *vi = vdev->priv;
+
+    /* remove the directory from sysfs */
+    sysfs_remove_group(&vdev->dev.kobj, &example_attr_group);
+
+    /* disable interrupts for vqs */
+    vdev->config->reset(vdev);
+
+    /* remove virtqueues */
+	vdev->config->del_vqs(vdev);
+
+    /* free memory */
+	kfree(vi);
+}
+
+
+/*
+ * vendor and device (+ subdevice and subvendor)
+ * identifies a device we support
+ */
+static struct virtio_device_id example_ids[] = {
+    {
+        .device = VIRTIO_ID_EXAMPLE,
+        .vendor = VIRTIO_DEV_ANY_ID,
+    },
+    { 0, },
+};
+
+/*
+ * id_table describe the device this driver support
+ * probe is called when a device we support exist and
+ * when we are chosen to drive it.
+ * remove is called when the driver is unloaded or
+ * when the device disappears
+ */
+static struct virtio_driver example = {
+	.driver.name =	"example",
+	.driver.owner =	THIS_MODULE,
+	.id_table =	example_ids,
+	.probe =	example_probe,
+	.remove =	example_remove,
+};
+
+
+
+//-----------------------------------------------------------------------------
+//                          overhead - must have
+//-----------------------------------------------------------------------------
+
+
+
+/* register driver in kernel pci framework */
+module_virtio_driver(example);
+MODULE_DEVICE_TABLE(virtio, example_ids);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Example virtio");
+MODULE_AUTHOR("Yoni Bettan");
+
-- 
2.17.2

_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linuxfoundation.org/mailman/listinfo/virtualization



[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux