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 ¶
- Variables
- func CheckQueueSize(queueSize int) error
- type AvailableRing
- type Descriptor
- type DescriptorTable
- type SplitQueue
- func (sq *SplitQueue) AvailableRing() *AvailableRing
- func (sq *SplitQueue) CallEventFD() int
- func (sq *SplitQueue) Close() error
- func (sq *SplitQueue) DescriptorTable() *DescriptorTable
- func (sq *SplitQueue) FreeDescriptorChain(head uint16) error
- func (sq *SplitQueue) GetDescriptorChain(head uint16) (outBuffers, inBuffers [][]byte, err error)
- func (sq *SplitQueue) KickEventFD() int
- func (sq *SplitQueue) OfferDescriptorChain(outBuffers [][]byte, numInBuffers int, waitFree bool) (uint16, error)
- func (sq *SplitQueue) Size() int
- func (sq *SplitQueue) UsedDescriptorChains() chan UsedElement
- func (sq *SplitQueue) UsedRing() *UsedRing
- type UsedElement
- type UsedRing
Constants ¶
This section is empty.
Variables ¶
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") )
var ErrQueueSizeInvalid = errors.New("queue size is invalid")
ErrQueueSizeInvalid is returned when a queue size is invalid.
Functions ¶
func CheckQueueSize ¶
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.