mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
docs: driver-api: virtio: virtio on Linux
Basic doc about Virtio on Linux and a short tutorial on Virtio drivers.
includes the following fixup:
virtio: fix virtio_config_ops kerneldocs
Fixes two warning messages when building htmldocs:
warning: duplicate section name 'Note'
warning: expecting prototype for virtio_config_ops().
Prototype was for vq_callback_t() instead
Message-Id: <20221010064359.1324353-2-ricardo.canuelo@collabora.com>
Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Message-Id: <20221220100035.2712449-1-ricardo.canuelo@collabora.com>
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Reviewed-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
committed by
Michael S. Tsirkin
parent
d5ff73bbb0
commit
d16c0cd273
197
Documentation/driver-api/virtio/writing_virtio_drivers.rst
Normal file
197
Documentation/driver-api/virtio/writing_virtio_drivers.rst
Normal file
@@ -0,0 +1,197 @@
|
||||
.. 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.
|
||||
Reference in New Issue
Block a user