Struct devices::virtio::queue::split_queue::SplitQueue
source · pub struct SplitQueue {
mem: GuestMemory,
event: Event,
interrupt: Interrupt,
size: u16,
vector: u16,
desc_table: GuestAddress,
avail_ring: GuestAddress,
used_ring: GuestAddress,
next_avail: Wrapping<u16>,
next_used: Wrapping<u16>,
features: u64,
last_used: Wrapping<u16>,
}
Expand description
An activated virtio queue with split queue layout.
Fields§
§mem: GuestMemory
§event: Event
§interrupt: Interrupt
§size: u16
The queue size in elements the driver selected. This is always guaranteed to be a power of two, as required for split virtqueues.
vector: u16
MSI-X vector for the queue. Don’t care for INTx
desc_table: GuestAddress
Guest physical address of the descriptor table
avail_ring: GuestAddress
Guest physical address of the available ring
used_ring: GuestAddress
Guest physical address of the used ring
next_avail: Wrapping<u16>
§next_used: Wrapping<u16>
§features: u64
§last_used: Wrapping<u16>
Implementations§
source§impl SplitQueue
impl SplitQueue
sourcepub fn new(
config: &QueueConfig,
mem: &GuestMemory,
event: Event,
interrupt: Interrupt
) -> Result<SplitQueue>
pub fn new( config: &QueueConfig, mem: &GuestMemory, event: Event, interrupt: Interrupt ) -> Result<SplitQueue>
Constructs an activated split virtio queue with the given configuration.
pub fn vhost_user_reclaim(&mut self, vring_base: u16)
pub fn next_avail_to_process(&self) -> u16
sourcepub fn size(&self) -> u16
pub fn size(&self) -> u16
Return the actual size of the queue, as the driver may not set up a queue as big as the device allows.
sourcepub fn desc_table(&self) -> GuestAddress
pub fn desc_table(&self) -> GuestAddress
Getter for descriptor area
sourcepub fn avail_ring(&self) -> GuestAddress
pub fn avail_ring(&self) -> GuestAddress
Getter for driver area
sourcepub fn used_ring(&self) -> GuestAddress
pub fn used_ring(&self) -> GuestAddress
Getter for device area
fn wrap_queue_index(&self, index: Wrapping<u16>) -> u16
fn ring_sizes( queue_size: u16, desc_table: GuestAddress, avail_ring: GuestAddress, used_ring: GuestAddress ) -> Vec<(GuestAddress, usize)>
fn get_avail_index(&self) -> Wrapping<u16>
fn set_avail_event(&mut self, avail_index: Wrapping<u16>)
fn get_avail_flag(&self, flag: u16) -> bool
fn get_used_event(&self) -> Wrapping<u16>
fn set_used_index(&mut self, used_index: Wrapping<u16>)
sourcepub fn peek(&mut self) -> Option<DescriptorChain>
pub fn peek(&mut self) -> Option<DescriptorChain>
Get the first available descriptor chain without removing it from the queue.
Call pop_peeked
to remove the returned descriptor chain from the queue.
sourcepub(super) fn pop_peeked(&mut self, _descriptor_chain: &DescriptorChain)
pub(super) fn pop_peeked(&mut self, _descriptor_chain: &DescriptorChain)
Remove the first available descriptor chain from the queue.
This function should only be called immediately following peek
and must be passed a
reference to the same DescriptorChain
returned by the most recent peek
.
sourcepub fn add_used(&mut self, desc_chain: DescriptorChain, len: u32)
pub fn add_used(&mut self, desc_chain: DescriptorChain, len: u32)
Puts an available descriptor head into the used ring for use by the guest.
sourcefn queue_wants_interrupt(&self) -> bool
fn queue_wants_interrupt(&self) -> bool
Returns if the queue should have an interrupt sent based on its state.
This function implements VIRTIO_RING_F_EVENT_IDX
, otherwise known as
interrupt suppression. The virtio spec provides the driver with a field,
used_event
, which says that once we write that descriptor (or several
in the case of a flurry of add_used
calls), we should send a
notification. Because the values involved wrap around u16::MAX
, and to
avoid checking the condition on every add_used
call, the math is a
little complicated.
The critical inequality is:
(next_used - 1) - used_event < next_used - last_used
For illustration purposes, we label it as A < B
, where
A = (next_used -1) - used_event
, and B = next_used - last_used
.
A
and B
represent two distances, measured in a wrapping ring of size
u16::MAX
. In the “send intr” case, the inequality is true. In the
“don’t send intr” case, the inequality is false. We must be very careful
in assigning a direction to the ring, so that when we
graph the subtraction operations, we are measuring the right distance
(similar to how DC circuits are analyzed).
The two distances are as follows:
-
A
is the distance between the driver’s requested notification point, and the current position in the ring. -
B
is the distance between the last time we notified the guest, and the current position in the ring.
If we graph these distances for the situation where we want to notify
the guest, and when we don’t want to notify the guest, we see that
A < B
becomes true the moment next_used - 1
passes used_event
. See
the graphs at the bottom of this comment block for a more visual
explanation.
Once an interrupt is sent, we have a final useful property: last_used
moves up next_used, which causes the inequality to be false. Thus, we
won’t send notifications again until used_event
is moved forward by
the driver.
Finally, let’s talk about a couple of ways to write this inequality that don’t work, and critically, explain why.
First, a naive reading of the virtio spec might lead us to ask: why not just use the following inequality:
next_used - 1 >= used_event
because that’s much simpler, right? The trouble is that the ring wraps, so it could be that a smaller index is actually ahead of a larger one. That’s why we have to use distances in the ring instead.
Second, one might look at the correct inequality:
(next_used - 1) - used_event < next_used - last_used
And try to simplify it to:
last_used - 1 < used_event
Functionally, this won’t work because next_used isn’t present at all anymore. (Notifications will never be sent.) But why is that? The algebra here appears to work out, but all semantic meaning is lost. There are two explanations for why this happens:
- The intuitive one: the terms in the inequality are not actually separable; in other words, (next_used - last_used) is an inseparable term, so subtracting next_used from both sides of the original inequality and zeroing them out is semantically invalid. But why aren’t they separable? See below.
- The theoretical one: canceling like terms relies a vector space law: a + x = b + x => a = b (cancellation law). For congruences / equality under modulo, this law is satisfied, but for inequalities under mod, it is not; therefore, we cannot cancel like terms.
┌──────────────────────────────────┐
│ │
│ │
│ │
│ ┌──────────── next_used - 1
│ │A x
│ │ ┌────────────x────────────┐
│ │ │ x │
│ │ │ │
│ │ │ │ │
│ │ │ │ │
│ used_event xxxx + ◄───┘ xxxxx last_used
│ │ │ │
│ │ Send intr │ │
│ │ │ │
│ └─────────────────────────┘ │
│ │
│ B │
└────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────┐
│ A │
│ ┌────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ │ │ │
│ │ │ │ │
used_event xxxx │ xxxxx last_used │
│ + ◄───┘ │ │ │
│ │ │ │
│ Don't send intr │ │ │
│ │ │ │
└───────────x────────────┘ │ │
x │ │
next_used - 1 │ │
│ │ B │ │
│ └────────────────────┘ │
│ │
└──────────────────────────────────┘
sourcepub fn trigger_interrupt(&mut self) -> bool
pub fn trigger_interrupt(&mut self) -> bool
inject interrupt into guest on this queue return true: interrupt is injected into guest for this queue false: interrupt isn’t injected
pub fn snapshot(&self) -> Result<Value>
pub fn restore( queue_value: Value, mem: &GuestMemory, event: Event, interrupt: Interrupt ) -> Result<SplitQueue>
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for SplitQueue
impl Send for SplitQueue
impl Sync for SplitQueue
impl Unpin for SplitQueue
impl !UnwindSafe for SplitQueue
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.