198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. SPDX-License-Identifier: GPL-2.0
 | |
| 
 | |
| .. _writing_virtio_drivers:
 | |
| 
 | |
| ======================
 | |
| Writing Virtio Drivers
 | |
| ======================
 | |
| 
 | |
| Introduction
 | |
| ============
 | |
| 
 | |
| This document serves as a basic guideline for driver programmers that
 | |
| need to hack a new virtio driver or understand the essentials of the
 | |
| existing ones. See :ref:`Virtio on Linux <virtio>` for a general
 | |
| overview of virtio.
 | |
| 
 | |
| 
 | |
| Driver boilerplate
 | |
| ==================
 | |
| 
 | |
| As a bare minimum, a virtio driver needs to register in the virtio bus
 | |
| and configure the virtqueues for the device according to its spec, the
 | |
| configuration of the virtqueues in the driver side must match the
 | |
| virtqueue definitions in the device. A basic driver skeleton could look
 | |
| like this::
 | |
| 
 | |
| 	#include <linux/virtio.h>
 | |
| 	#include <linux/virtio_ids.h>
 | |
| 	#include <linux/virtio_config.h>
 | |
| 	#include <linux/module.h>
 | |
| 
 | |
| 	/* device private data (one per device) */
 | |
| 	struct virtio_dummy_dev {
 | |
| 		struct virtqueue *vq;
 | |
| 	};
 | |
| 
 | |
| 	static void virtio_dummy_recv_cb(struct virtqueue *vq)
 | |
| 	{
 | |
| 		struct virtio_dummy_dev *dev = vq->vdev->priv;
 | |
| 		char *buf;
 | |
| 		unsigned int len;
 | |
| 
 | |
| 		while ((buf = virtqueue_get_buf(dev->vq, &len)) != NULL) {
 | |
| 			/* process the received data */
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static int virtio_dummy_probe(struct virtio_device *vdev)
 | |
| 	{
 | |
| 		struct virtio_dummy_dev *dev = NULL;
 | |
| 
 | |
| 		/* initialize device data */
 | |
| 		dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
 | |
| 		if (!dev)
 | |
| 			return -ENOMEM;
 | |
| 
 | |
| 		/* the device has a single virtqueue */
 | |
| 		dev->vq = virtio_find_single_vq(vdev, virtio_dummy_recv_cb, "input");
 | |
| 		if (IS_ERR(dev->vq)) {
 | |
| 			kfree(dev);
 | |
| 			return PTR_ERR(dev->vq);
 | |
| 
 | |
| 		}
 | |
| 		vdev->priv = dev;
 | |
| 
 | |
| 		/* from this point on, the device can notify and get callbacks */
 | |
| 		virtio_device_ready(vdev);
 | |
| 
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	static void virtio_dummy_remove(struct virtio_device *vdev)
 | |
| 	{
 | |
| 		struct virtio_dummy_dev *dev = vdev->priv;
 | |
| 
 | |
| 		/*
 | |
| 		 * disable vq interrupts: equivalent to
 | |
| 		 * vdev->config->reset(vdev)
 | |
| 		 */
 | |
| 		virtio_reset_device(vdev);
 | |
| 
 | |
| 		/* detach unused buffers */
 | |
| 		while ((buf = virtqueue_detach_unused_buf(dev->vq)) != NULL) {
 | |
| 			kfree(buf);
 | |
| 		}
 | |
| 
 | |
| 		/* remove virtqueues */
 | |
| 		vdev->config->del_vqs(vdev);
 | |
| 
 | |
| 		kfree(dev);
 | |
| 	}
 | |
| 
 | |
| 	static const struct virtio_device_id id_table[] = {
 | |
| 		{ VIRTIO_ID_DUMMY, VIRTIO_DEV_ANY_ID },
 | |
| 		{ 0 },
 | |
| 	};
 | |
| 
 | |
| 	static struct virtio_driver virtio_dummy_driver = {
 | |
| 		.driver.name =  KBUILD_MODNAME,
 | |
| 		.driver.owner = THIS_MODULE,
 | |
| 		.id_table =     id_table,
 | |
| 		.probe =        virtio_dummy_probe,
 | |
| 		.remove =       virtio_dummy_remove,
 | |
| 	};
 | |
| 
 | |
| 	module_virtio_driver(virtio_dummy_driver);
 | |
| 	MODULE_DEVICE_TABLE(virtio, id_table);
 | |
| 	MODULE_DESCRIPTION("Dummy virtio driver");
 | |
| 	MODULE_LICENSE("GPL");
 | |
| 
 | |
| The device id ``VIRTIO_ID_DUMMY`` here is a placeholder, virtio drivers
 | |
| should be added only for devices that are defined in the spec, see
 | |
| include/uapi/linux/virtio_ids.h. Device ids need to be at least reserved
 | |
| in the virtio spec before being added to that file.
 | |
| 
 | |
| If your driver doesn't have to do anything special in its ``init`` and
 | |
| ``exit`` methods, you can use the module_virtio_driver() helper to
 | |
| reduce the amount of boilerplate code.
 | |
| 
 | |
| The ``probe`` method does the minimum driver setup in this case
 | |
| (memory allocation for the device data) and initializes the
 | |
| virtqueue. virtio_device_ready() is used to enable the virtqueue and to
 | |
| notify the device that the driver is ready to manage the device
 | |
| ("DRIVER_OK"). The virtqueues are anyway enabled automatically by the
 | |
| core after ``probe`` returns.
 | |
| 
 | |
| .. kernel-doc:: include/linux/virtio_config.h
 | |
|     :identifiers: virtio_device_ready
 | |
| 
 | |
| In any case, the virtqueues need to be enabled before adding buffers to
 | |
| them.
 | |
| 
 | |
| Sending and receiving data
 | |
| ==========================
 | |
| 
 | |
| The virtio_dummy_recv_cb() callback in the code above will be triggered
 | |
| when the device notifies the driver after it finishes processing a
 | |
| descriptor or descriptor chain, either for reading or writing. However,
 | |
| that's only the second half of the virtio device-driver communication
 | |
| process, as the communication is always started by the driver regardless
 | |
| of the direction of the data transfer.
 | |
| 
 | |
| To configure a buffer transfer from the driver to the device, first you
 | |
| have to add the buffers -- packed as `scatterlists` -- to the
 | |
| appropriate virtqueue using any of the virtqueue_add_inbuf(),
 | |
| virtqueue_add_outbuf() or virtqueue_add_sgs(), depending on whether you
 | |
| need to add one input `scatterlist` (for the device to fill in), one
 | |
| output `scatterlist` (for the device to consume) or multiple
 | |
| `scatterlists`, respectively. Then, once the virtqueue is set up, a call
 | |
| to virtqueue_kick() sends a notification that will be serviced by the
 | |
| hypervisor that implements the device::
 | |
| 
 | |
| 	struct scatterlist sg[1];
 | |
| 	sg_init_one(sg, buffer, BUFLEN);
 | |
| 	virtqueue_add_inbuf(dev->vq, sg, 1, buffer, GFP_ATOMIC);
 | |
| 	virtqueue_kick(dev->vq);
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_add_inbuf
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_add_outbuf
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_add_sgs
 | |
| 
 | |
| Then, after the device has read or written the buffers prepared by the
 | |
| driver and notifies it back, the driver can call virtqueue_get_buf() to
 | |
| read the data produced by the device (if the virtqueue was set up with
 | |
| input buffers) or simply to reclaim the buffers if they were already
 | |
| consumed by the device:
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_get_buf_ctx
 | |
| 
 | |
| The virtqueue callbacks can be disabled and re-enabled using the
 | |
| virtqueue_disable_cb() and the family of virtqueue_enable_cb() functions
 | |
| respectively. See drivers/virtio/virtio_ring.c for more details:
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_disable_cb
 | |
| 
 | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c
 | |
|     :identifiers: virtqueue_enable_cb
 | |
| 
 | |
| But note that some spurious callbacks can still be triggered under
 | |
| certain scenarios. The way to disable callbacks reliably is to reset the
 | |
| device or the virtqueue (virtio_reset_device()).
 | |
| 
 | |
| 
 | |
| References
 | |
| ==========
 | |
| 
 | |
| _`[1]` Virtio Spec v1.2:
 | |
| https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
 | |
| 
 | |
| Check for later versions of the spec as well.
 |