virtqueue

package
v0.0.0-...-1028ecd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2025 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package virtqueue implements the driver-side for a virtio queue as described in the specification: https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-270006 This package does not make assumptions about the device that consumes the queue. It rather just allocates the queue structures in memory and provides methods to interact with it.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrDescriptorChainEmpty is returned when a descriptor chain would contain
	// no buffers, which is not allowed.
	ErrDescriptorChainEmpty = errors.New("empty descriptor chains are not allowed")

	// ErrNotEnoughFreeDescriptors is returned when the free descriptors are
	// exhausted, meaning that the queue is full.
	ErrNotEnoughFreeDescriptors = errors.New("not enough free descriptors, queue is full")

	// ErrInvalidDescriptorChain is returned when a descriptor chain is not
	// valid for a given operation.
	ErrInvalidDescriptorChain = errors.New("invalid descriptor chain")
)
View Source
var ErrQueueSizeInvalid = errors.New("queue size is invalid")

ErrQueueSizeInvalid is returned when a queue size is invalid.

Functions

func CheckQueueSize

func CheckQueueSize(queueSize int) error

CheckQueueSize checks if the given value would be a valid size for a virtqueue and returns an ErrQueueSizeInvalid, if not.

Types

type AvailableRing

type AvailableRing struct {
	// contains filtered or unexported fields
}

AvailableRing is used by the driver to offer descriptor chains to the device. Each ring entry refers to the head of a descriptor chain. It is only written to by the driver and read by the device.

Because the size of the ring depends on the queue size, we cannot define a Go struct with a static size that maps to the memory of the ring. Instead, this struct only contains pointers to the corresponding memory areas.

func (*AvailableRing) Address

func (r *AvailableRing) Address() uintptr

Address returns the pointer to the beginning of the ring in memory. Do not modify the memory directly to not interfere with this implementation.

type Descriptor

type Descriptor struct {
	// contains filtered or unexported fields
}

Descriptor describes (a part of) a buffer which is either read-only for the device or write-only for the device (depending on [descriptorFlagWritable]). Multiple descriptors can be chained to produce a "descriptor chain" that can contain both device-readable and device-writable buffers. Device-readable descriptors always come first in a chain. A single, large buffer may be split up by chaining multiple similar descriptors that reference different memory pages. This is required, because buffers may exceed a single page size and the memory accessed by the device is expected to be continuous.

type DescriptorTable

type DescriptorTable struct {
	// contains filtered or unexported fields
}

DescriptorTable is a table that holds [Descriptor]s, addressed via their index in the slice.

func (*DescriptorTable) Address

func (dt *DescriptorTable) Address() uintptr

Address returns the pointer to the beginning of the descriptor table in memory. Do not modify the memory directly to not interfere with this implementation.

func (*DescriptorTable) BufferAddresses

func (dt *DescriptorTable) BufferAddresses() []uintptr

BufferAddresses returns pointers to all memory pages used by the descriptor table to store buffers, independent of whether descriptors are currently in use or not. These pointers can be helpful to set up memory mappings. Do not use them to access or modify the memory in any way. Each pointer points to a whole memory page. Use os.Getpagesize to get the page size.

type SplitQueue

type SplitQueue struct {
	// contains filtered or unexported fields
}

SplitQueue is a virtqueue that consists of several parts, where each part is writeable by either the driver or the device, but not both.

func NewSplitQueue

func NewSplitQueue(queueSize int) (_ *SplitQueue, err error)

NewSplitQueue allocates a new SplitQueue in memory. The given queue size specifies the number of entries/buffers the queue can hold. This also affects the memory consumption.

func (*SplitQueue) AvailableRing

func (sq *SplitQueue) AvailableRing() *AvailableRing

AvailableRing returns the AvailableRing behind this queue.

func (*SplitQueue) CallEventFD

func (sq *SplitQueue) CallEventFD() int

CallEventFD returns the call event file descriptor behind this queue. The returned file descriptor should be used with great care to not interfere with this implementation.

func (*SplitQueue) Close

func (sq *SplitQueue) Close() error

Close releases all resources used for this queue. The implementation will try to release as many resources as possible and collect potential errors before returning them.

func (*SplitQueue) DescriptorTable

func (sq *SplitQueue) DescriptorTable() *DescriptorTable

DescriptorTable returns the DescriptorTable behind this queue.

func (*SplitQueue) FreeDescriptorChain

func (sq *SplitQueue) FreeDescriptorChain(head uint16) error

FreeDescriptorChain frees the descriptor chain with the given head index. The head index must be one that was returned by a previous call to SplitQueue.OfferDescriptorChain and the descriptor chain must not have been freed yet.

This creates new room in the queue which can be used by following SplitQueue.OfferDescriptorChain calls. When there are outstanding calls for SplitQueue.OfferDescriptorChain that are waiting for free room in the queue, they may become unblocked by this.

func (*SplitQueue) GetDescriptorChain

func (sq *SplitQueue) GetDescriptorChain(head uint16) (outBuffers, inBuffers [][]byte, err error)

GetDescriptorChain returns the device-readable buffers (out buffers) and device-writable buffers (in buffers) of the descriptor chain with the given head index. The head index must be one that was returned by a previous call to SplitQueue.OfferDescriptorChain and the descriptor chain must not have been freed yet.

Be careful to only access the returned buffer slices when the device is no longer using them. They must not be accessed after SplitQueue.FreeDescriptorChain has been called.

func (*SplitQueue) KickEventFD

func (sq *SplitQueue) KickEventFD() int

KickEventFD returns the kick event file descriptor behind this queue. The returned file descriptor should be used with great care to not interfere with this implementation.

func (*SplitQueue) OfferDescriptorChain

func (sq *SplitQueue) OfferDescriptorChain(outBuffers [][]byte, numInBuffers int, waitFree bool) (uint16, error)

OfferDescriptorChain offers a descriptor chain to the device which contains a number of device-readable buffers (out buffers) and device-writable buffers (in buffers).

All buffers in the outBuffers slice will be concatenated by chaining descriptors, one for each buffer in the slice. When a buffer is too large to fit into a single descriptor (limited by the system's page size), it will be split up into multiple descriptors within the chain. When numInBuffers is greater than zero, the given number of device-writable descriptors will be appended to the end of the chain, each referencing a whole memory page (see os.Getpagesize).

When the queue is full and no more descriptor chains can be added, a wrapped ErrNotEnoughFreeDescriptors will be returned. If you set waitFree to true, this method will handle this error and will block instead until there are enough free descriptors again.

After defining the descriptor chain in the DescriptorTable, the index of the head of the chain will be made available to the device using the AvailableRing and will be returned by this method. Callers should read from the SplitQueue.UsedDescriptorChains channel to be notified when the descriptor chain was used by the device and should free the used descriptor chains again using SplitQueue.FreeDescriptorChain when they're done with them. When this does not happen, the queue will run full and any further calls to SplitQueue.OfferDescriptorChain will stall.

func (*SplitQueue) Size

func (sq *SplitQueue) Size() int

Size returns the size of this queue, which is the number of entries/buffers this queue can hold.

func (*SplitQueue) UsedDescriptorChains

func (sq *SplitQueue) UsedDescriptorChains() chan UsedElement

UsedDescriptorChains returns the channel that receives [UsedElement]s for all descriptor chains that were used by the device.

Users of the SplitQueue should read from this channel, handle the used descriptor chains and free them using SplitQueue.FreeDescriptorChain when they're done with them. When this does not happen, the queue will run full and any further calls to SplitQueue.OfferDescriptorChain will stall.

When SplitQueue.Close is called, this channel will be closed as well.

func (*SplitQueue) UsedRing

func (sq *SplitQueue) UsedRing() *UsedRing

UsedRing returns the UsedRing behind this queue.

type UsedElement

type UsedElement struct {
	// DescriptorIndex is the index of the head of the used descriptor chain in
	// the [DescriptorTable].
	// The index is 32-bit here for padding reasons.
	DescriptorIndex uint32
	// Length is the number of bytes written into the device writable portion of
	// the buffer described by the descriptor chain.
	Length uint32
}

UsedElement is an element of the UsedRing and describes a descriptor chain that was used by the device.

type UsedRing

type UsedRing struct {
	// contains filtered or unexported fields
}

UsedRing is where the device returns descriptor chains once it is done with them. Each ring entry is a UsedElement. It is only written to by the device and read by the driver.

Because the size of the ring depends on the queue size, we cannot define a Go struct with a static size that maps to the memory of the ring. Instead, this struct only contains pointers to the corresponding memory areas.

func (*UsedRing) Address

func (r *UsedRing) Address() uintptr

Address returns the pointer to the beginning of the ring in memory. Do not modify the memory directly to not interfere with this implementation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL