* Copyright Rusty Russell IBM Corporation 2007.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* $FreeBSD$
*/
#ifndef VIRTIO_RING_H
#define VIRTIO_RING_H
#include <metal/compiler.h>
#include <stddef.h>
#if defined __cplusplus
extern "C" {
#endif
#define VRING_DESC_F_NEXT 1U
#define VRING_DESC_F_WRITE 2U
#define VRING_DESC_F_INDIRECT 4U
* when you add a buffer. It's unreliable, so it's simply an
* optimization. Guest will still kick if it's out of buffers.
*/
#define VRING_USED_F_NO_NOTIFY 1U
* interrupt me when you consume a buffer. It's unreliable, so it's
* simply an optimization.
*/
#define VRING_AVAIL_F_NO_INTERRUPT 1U
* When using pre-virtio 1.0 layout, these fall out naturally.
*/
#define VRING_AVAIL_ALIGN_SIZE 2
#define VRING_USED_ALIGN_SIZE 4
#define VRING_DESC_ALIGN_SIZE 16
* @brief VirtIO ring descriptors.
*
* The descriptor table refers to the buffers the driver is using for the
* device. addr is a physical address, and the buffers can be chained via \ref next.
* Each descriptor describes a buffer which is read-only for the device
* (“device-readable”) or write-only for the device (“device-writable”), but a
* chain of descriptors can contain both device-readable and device-writable
* buffers.
*/
struct vring_desc {
uint64_t addr;
uint32_t len;
uint16_t flags;
uint16_t next;
};
* @brief Used to offer buffers to the device.
*
* Each ring entry refers to the head of a descriptor chain. It is only
* written by the driver and read by the device.
*/
struct vring_avail {
uint16_t flags;
* Indicates where the driver puts the next descriptor entry in the
* ring (modulo the queue size)
*/
uint16_t idx;
uint16_t ring[0];
};
struct vring_used_elem {
union {
uint16_t event;
uint32_t id;
} u;
uint32_t len;
};
typedef struct vring_used_elem metal_align(VRING_USED_ALIGN_SIZE) vring_used_elem_t;
* @brief The device returns buffers to this structure when done with them
*
* The structure is only written to by the device, and read by the driver.
*/
struct vring_used {
uint16_t flags;
* Indicates where the driver puts the next descriptor entry in the
* ring (modulo the queue size)
*/
uint16_t idx;
vring_used_elem_t ring[0];
};
* The ring element addresses are passed between components with different
* alignments assumptions. Thus, we might need to decrease the compiler-selected
* alignment, and so must use a typedef to make sure the aligned attribute
* actually takes hold:
*
* https://gcc.gnu.org/onlinedocs//gcc/Common-Type-Attributes.html#Common-Type-Attributes
*
* When used on a struct, or struct member, the aligned attribute can only
* increase the alignment; in order to decrease it, the packed attribute must
* be specified as well. When used as part of a typedef, the aligned attribute
* can both increase and decrease alignment, and specifying the packed
* attribute generates a warning.
*/
typedef struct vring_desc metal_align(VRING_DESC_ALIGN_SIZE) vring_desc_t;
typedef struct vring_avail metal_align(VRING_AVAIL_ALIGN_SIZE) vring_avail_t;
typedef struct vring_used metal_align(VRING_USED_ALIGN_SIZE) vring_used_t;
* @brief The virtqueue layout structure
*
* Each virtqueue consists of; descriptor table, available ring, used ring,
* where each part is physically contiguous in guest memory.
*
* When the driver wants to send a buffer to the device, it fills in a slot in
* the descriptor table (or chains several together), and writes the descriptor
* index into the available ring. It then notifies the device. When the device
* has finished a buffer, it writes the descriptor index into the used ring,
* and sends an interrupt.
*
* The standard layout for the ring is a continuous chunk of memory which
* looks like this. We assume num is a power of 2.
*
* struct vring {
* // The actual descriptors (16 bytes each)
* struct vring_desc desc[num];
*
* // A ring of available descriptor heads with free-running index.
* __u16 avail_flags;
* __u16 avail_idx;
* __u16 available[num];
* __u16 used_event_idx;
*
* // Padding to the next align boundary.
* char pad[];
*
* // A ring of used descriptor heads with free-running index.
* __u16 used_flags;
* __u16 used_idx;
* struct vring_used_elem used[num];
* __u16 avail_event_idx;
* };
*
* NOTE: for VirtIO PCI, align is 4096.
*/
struct vring {
* The maximum number of buffer descriptors in the virtqueue.
* The value is always a power of 2.
*/
unsigned int num;
vring_desc_t *desc;
vring_avail_t *avail;
vring_used_t *used;
};
* We publish the used event index at the end of the available ring, and vice
* versa. They are at the end for backwards compatibility.
*/
#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
#define vring_avail_event(vr) ((vr)->used->ring[(vr)->num].u.event)
static inline size_t vring_size(unsigned int num, unsigned long align)
{
size_t size;
size = num * sizeof(struct vring_desc);
size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) +
sizeof(uint16_t);
size = (size + align - 1) & ~(align - 1);
size += sizeof(struct vring_used) +
(num * sizeof(struct vring_used_elem)) + sizeof(uint16_t);
return size;
}
static inline void
vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
{
vr->num = num;
vr->desc = (struct vring_desc *)p;
vr->avail = (struct vring_avail *)(p + num * sizeof(struct vring_desc));
vr->used = (struct vring_used *)
(((unsigned long)(void *)&vr->avail->ring[num] + sizeof(uint16_t) +
align - 1) & ~(align - 1));
}
* The following is used with VIRTIO_RING_F_EVENT_IDX.
*
* Assuming a given event_idx value from the other size, if we have
* just incremented index from old to new_idx, should we trigger an
* event?
*/
static inline int
vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
{
return (uint16_t)(new_idx - event_idx - 1) <
(uint16_t)(new_idx - old);
}
#if defined __cplusplus
}
#endif
#endif