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

source

pub fn new( config: &QueueConfig, mem: &GuestMemory, event: Event, interrupt: Interrupt ) -> Result<SplitQueue>

Constructs an activated split virtio queue with the given configuration.

source

pub fn vhost_user_reclaim(&mut self, vring_base: u16)

source

pub fn next_avail_to_process(&self) -> u16

source

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.

source

pub fn vector(&self) -> u16

Getter for vector field

source

pub fn desc_table(&self) -> GuestAddress

Getter for descriptor area

source

pub fn avail_ring(&self) -> GuestAddress

Getter for driver area

source

pub fn used_ring(&self) -> GuestAddress

Getter for device area

source

pub fn event(&self) -> &Event

Get a reference to the queue’s “kick event”

source

pub fn interrupt(&self) -> &Interrupt

Get a reference to the queue’s interrupt

source

fn wrap_queue_index(&self, index: Wrapping<u16>) -> u16

source

fn ring_sizes( queue_size: u16, desc_table: GuestAddress, avail_ring: GuestAddress, used_ring: GuestAddress ) -> Vec<(GuestAddress, usize)>

source

fn get_avail_index(&self) -> Wrapping<u16>

source

fn set_avail_event(&mut self, avail_index: Wrapping<u16>)

source

fn get_avail_flag(&self, flag: u16) -> bool

source

fn get_used_event(&self) -> Wrapping<u16>

source

fn set_used_index(&mut self, used_index: Wrapping<u16>)

source

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.

source

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.

source

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.

source

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 │          │
                             │  └────────────────────┘          │
                             │                                  │
                             └──────────────────────────────────┘
source

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

source

pub fn snapshot(&self) -> Result<Value>

source

pub fn restore( queue_value: Value, mem: &GuestMemory, event: Event, interrupt: Interrupt ) -> Result<SplitQueue>

Trait Implementations§

source§

impl Debug for SplitQueue

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V