// Copyright 2017 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::num::Wrapping;5use std::sync::atomic::fence;6use std::sync::atomic::AtomicU16;7use std::sync::atomic::Ordering;89use anyhow::bail;10use anyhow::Context;11use anyhow::Result;12use base::error;13use base::Event;14use data_model::Le32;15use serde::Deserialize;16use serde::Serialize;17use snapshot::AnySnapshot;18use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;19use vm_memory::GuestAddress;20use vm_memory::GuestMemory;21use zerocopy::FromBytes;22use zerocopy::Immutable;23use zerocopy::IntoBytes;24use zerocopy::KnownLayout;2526use crate::virtio::DescriptorChain;27use crate::virtio::Interrupt;28use crate::virtio::QueueConfig;29use crate::virtio::SplitDescriptorChain;3031#[allow(dead_code)]32const VIRTQ_USED_F_NO_NOTIFY: u16 = 0x1;33#[allow(dead_code)]34const VIRTQ_AVAIL_F_NO_INTERRUPT: u16 = 0x1;3536/// An activated virtio queue with split queue layout.37#[derive(Debug)]38pub struct SplitQueue {39mem: GuestMemory,4041event: Event,42interrupt: Interrupt,4344/// The queue size in elements the driver selected. This is always guaranteed to be a power of45/// two, as required for split virtqueues.46size: u16,4748/// MSI-X vector for the queue. Don't care for INTx49vector: u16,5051/// Guest physical address of the descriptor table52desc_table: GuestAddress,5354/// Guest physical address of the available ring55avail_ring: GuestAddress,5657/// Guest physical address of the used ring58used_ring: GuestAddress,5960next_avail: Wrapping<u16>,61next_used: Wrapping<u16>,6263// Device feature bits accepted by the driver64features: u64,65last_used: Wrapping<u16>,66}6768#[derive(Serialize, Deserialize)]69pub struct SplitQueueSnapshot {70size: u16,71vector: u16,72desc_table: GuestAddress,73avail_ring: GuestAddress,74used_ring: GuestAddress,75next_avail: Wrapping<u16>,76next_used: Wrapping<u16>,77features: u64,78last_used: Wrapping<u16>,79}8081#[repr(C)]82#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]83struct virtq_used_elem {84id: Le32,85len: Le32,86}8788impl SplitQueue {89/// Constructs an activated split virtio queue with the given configuration.90pub fn new(91config: &QueueConfig,92mem: &GuestMemory,93event: Event,94interrupt: Interrupt,95) -> Result<SplitQueue> {96let size = config.size();97if !size.is_power_of_two() {98bail!("split queue size {size} is not a power of 2");99}100101let desc_table = config.desc_table();102let avail_ring = config.avail_ring();103let used_ring = config.used_ring();104105// Validate addresses and queue size to ensure that address calculation won't overflow.106let ring_sizes = Self::ring_sizes(size, desc_table, avail_ring, used_ring);107let rings = ring_sizes108.iter()109.zip(vec!["descriptor table", "available ring", "used ring"]);110111for ((addr, size), name) in rings {112if addr.checked_add(*size as u64).is_none() {113bail!(114"virtio queue {} goes out of bounds: start:0x{:08x} size:0x{:08x}",115name,116addr.offset(),117size,118);119}120}121122Ok(SplitQueue {123mem: mem.clone(),124event,125interrupt,126size,127vector: config.vector(),128desc_table: config.desc_table(),129avail_ring: config.avail_ring(),130used_ring: config.used_ring(),131features: config.acked_features(),132next_avail: config.next_avail(),133next_used: config.next_used(),134135// WARNING: last_used controls interrupt suppression136// (VIRTIO_RING_F_EVENT_IDX). The only safe value initial value is137// zero (unless restoring a snapshot and the value that was stored138// on the device is known; however we do not bother with that in our139// snapshot system since it is much simpler to just use the zero140// value and send a potentially spurious interrupt on restore).141last_used: Wrapping(0),142})143}144145pub fn vhost_user_reclaim(&mut self, vring_base: u16) {146self.next_avail = Wrapping(vring_base);147// The vhost-user spec says:148//149// For the Used Ring, the device only needs the next descriptor index at which to put150// new descriptors, which is the value in the vring structure in memory, so this value151// is not covered by this message.152//153// So, we read the value from guest memory.154let used_index_addr = self.used_ring.unchecked_add(2);155self.next_used = self156.mem157.read_obj_from_addr_volatile(used_index_addr)158.unwrap();159160// Since the backend has not told us what its actual last_used value161// was, we have to assume that an interrupt must be sent when next162// available descriptor is used, so we set this to zero.163//164// But wait, one might ask, why can't we just assume the vhost-user165// backend has already sent interrupts for any descriptors it marked166// used before it stopped processing the queue? Then we could just167// initialize last_used as `last_used == next_used`, which would skip168// spurious interrupts and be more efficient. Right?169//170// If VIRTIO_RING_F_EVENT_IDX is enabled, then no. The reason is the171// device could be in an interrupt suppressed state and so it may indeed172// have marked some descriptors used, but not yet sent an interrupt for173// them. Once we set last_used = next_used, no interrupts will be sent174// to the driver until the driver updates next_used (see175// queue_wants_interrupt for details), but the driver will176// never wake up the device isn't sending any interrupts. Thus, the177// device stalls.178//179// NOTE: this value is not used by the snapshot/restore process, but we180// still want to pick a reasonable value here in case it is used in the181// future.182self.last_used = Wrapping(0);183}184185pub fn next_avail_to_process(&self) -> u16 {186self.next_avail.0187}188189/// Return the actual size of the queue, as the driver may not set up a190/// queue as big as the device allows.191pub fn size(&self) -> u16 {192self.size193}194195/// Getter for vector field196pub fn vector(&self) -> u16 {197self.vector198}199200/// Getter for descriptor area201pub fn desc_table(&self) -> GuestAddress {202self.desc_table203}204205/// Getter for driver area206pub fn avail_ring(&self) -> GuestAddress {207self.avail_ring208}209210/// Getter for device area211pub fn used_ring(&self) -> GuestAddress {212self.used_ring213}214215/// Get a reference to the queue's "kick event"216pub fn event(&self) -> &Event {217&self.event218}219220/// Get a reference to the queue's interrupt221pub fn interrupt(&self) -> &Interrupt {222&self.interrupt223}224225// Return `index` modulo the currently configured queue size.226fn wrap_queue_index(&self, index: Wrapping<u16>) -> u16 {227// We know that `self.size` is a power of two (enforced by `new()`), so the modulus can228// be calculated with a bitmask rather than actual division.229debug_assert!(self.size.is_power_of_two());230index.0 & self.size.wrapping_sub(1)231}232233fn ring_sizes(234queue_size: u16,235desc_table: GuestAddress,236avail_ring: GuestAddress,237used_ring: GuestAddress,238) -> Vec<(GuestAddress, usize)> {239let queue_size = queue_size as usize;240vec![241(desc_table, 16 * queue_size),242(avail_ring, 6 + 2 * queue_size),243(used_ring, 6 + 8 * queue_size),244]245}246247// Set the `avail_event` field in the used ring.248//249// This allows the device to inform the driver that driver-to-device notification250// (kicking the ring) is not necessary until the driver reaches the `avail_index` descriptor.251//252// This value is only used if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.253fn set_avail_event(&mut self, avail_index: Wrapping<u16>) {254fence(Ordering::SeqCst);255256let avail_event_addr = self.used_ring.unchecked_add(4 + 8 * u64::from(self.size));257self.mem258.write_obj_at_addr_volatile(avail_index.0, avail_event_addr)259.unwrap();260}261262// Query the value of a single-bit flag in the available ring.263//264// Returns `true` if `flag` is currently set (by the driver) in the available ring flags.265fn get_avail_flag(&self, flag: u16) -> bool {266fence(Ordering::SeqCst);267268let avail_flags: u16 = self269.mem270.read_obj_from_addr_volatile(self.avail_ring)271.unwrap();272273avail_flags & flag == flag274}275276// Get the `used_event` field in the available ring.277//278// The returned value is the index of the next descriptor chain entry for which the driver279// needs to be notified upon use. Entries before this index may be used without notifying280// the driver.281//282// This value is only valid if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.283fn get_used_event(&self) -> Wrapping<u16> {284fence(Ordering::SeqCst);285286let used_event_addr = self.avail_ring.unchecked_add(4 + 2 * u64::from(self.size));287let used_event: u16 = self288.mem289.read_obj_from_addr_volatile(used_event_addr)290.unwrap();291292Wrapping(used_event)293}294295/// Get the first available descriptor chain without removing it from the queue.296/// Call `pop_peeked` to remove the returned descriptor chain from the queue.297pub fn peek(&mut self) -> Option<DescriptorChain> {298// Get a `VolatileSlice` covering the `struct virtq_avail` fixed header (`flags` and `idx`)299// and variable-length `ring`. This ensures that the raw pointers generated below point into300// valid `GuestMemory` regions.301let avail_ring_size = 2 * size_of::<u16>() + (size_of::<u16>() * usize::from(self.size));302let avail_ring_vslice = self303.mem304.get_slice_at_addr(self.avail_ring, avail_ring_size)305.unwrap();306307// SAFETY: offset of `virtq_avail.idx` (2) is always within the `VolatileSlice` bounds.308let avail_index_ptr = unsafe { avail_ring_vslice.as_mut_ptr().add(2) } as *mut u16;309// SAFETY: `GuestMemory::get_slice_at_addr()` returns a valid `VolatileSlice`, and310// `avail_index_ptr` is a valid `*mut u16` contained within that slice.311let avail_index_atomic = unsafe { AtomicU16::from_ptr(avail_index_ptr) };312313// Check if the driver has published any new descriptors beyond `self.next_avail`. This uses314// a `Relaxed` load because we do not need a memory barrier if there are no new descriptors.315// If the ring is not empty, the `fence()` below will provide the necessary ordering,316// pairing with the write memory barrier in the driver.317let avail_index: u16 = avail_index_atomic.load(Ordering::Relaxed);318let next_avail = self.next_avail;319if next_avail.0 == avail_index {320return None;321}322323// This fence ensures that subsequent reads from the descriptor do not324// get reordered and happen only after fetching the available_index and325// checking that there is a slot available.326fence(Ordering::Acquire);327328// Calculate the offset of `ring[next_avail % size]` within `struct virtq_avail`.329let ring_offset = 4 + (usize::from(self.wrap_queue_index(next_avail)) * 2);330debug_assert!(ring_offset + size_of::<u16>() <= avail_ring_size);331// SAFETY: The available ring index was wrapped to fall within the queue size above, so332// `ring_offset` is always in bounds.333let ring_ptr = unsafe { avail_ring_vslice.as_ptr().add(ring_offset) } as *const u16;334// SAFETY: `ring_ptr` is a valid `*const u16` within `avail_ring_vslice`.335let descriptor_index: u16 = unsafe { std::ptr::read_volatile(ring_ptr) };336337let chain =338SplitDescriptorChain::new(&self.mem, self.desc_table, self.size, descriptor_index);339DescriptorChain::new(chain, &self.mem, descriptor_index)340.map_err(|e| {341error!("{:#}", e);342e343})344.ok()345}346347/// Remove the first available descriptor chain from the queue.348/// This function should only be called immediately following `peek` and must be passed a349/// reference to the same `DescriptorChain` returned by the most recent `peek`.350pub(super) fn pop_peeked(&mut self, _descriptor_chain: &DescriptorChain) {351self.next_avail += Wrapping(1);352if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {353self.set_avail_event(self.next_avail);354}355}356357pub(super) fn try_pop_length(&mut self, length: usize) -> Option<Vec<DescriptorChain>> {358let mut remain_len = length;359let mut descriptors = vec![];360while remain_len > 0 {361match self.peek() {362Some(desc) => {363let available_bytes = desc.writer.available_bytes();364descriptors.push(desc);365self.next_avail += Wrapping(1);366if available_bytes >= remain_len {367if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {368self.set_avail_event(self.next_avail);369}370return Some(descriptors);371} else {372remain_len -= available_bytes;373}374}375None => {376if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {377self.set_avail_event(self.next_avail);378}379// Reverse the effect of pop380self.next_avail -= Wrapping(descriptors.len() as u16);381return None;382}383}384}385None386}387388/// Puts multiple available descriptor heads into the used ring for use by the guest.389pub fn add_used_with_bytes_written_batch(390&mut self,391desc_chains: impl IntoIterator<Item = (DescriptorChain, u32)>,392) {393// Get a `VolatileSlice` covering the `struct virtq_used` fixed header (`flags` and `idx`)394// and variable-length `ring`. This ensures that the raw pointers generated below point into395// valid `GuestMemory` regions.396let used_ring_size =3972 * size_of::<u16>() + (size_of::<virtq_used_elem>() * usize::from(self.size));398let used_ring_vslice = self399.mem400.get_slice_at_addr(self.used_ring, used_ring_size)401.unwrap();402403// SAFETY: `elems_ptr` is always a valid pointer due to `used_ring_vslice()`.404let elems_ptr = unsafe { used_ring_vslice.as_mut_ptr().add(4) } as *mut virtq_used_elem;405406// SAFETY: `used_index_ptr` is always a valid pointer due to `used_ring_vslice()`.407let used_index_ptr = unsafe { used_ring_vslice.as_mut_ptr().add(2) } as *mut u16;408409// SAFETY: `used_index_ptr` is always a valid pointer410let used_index_atomic = unsafe { AtomicU16::from_ptr(used_index_ptr) };411412let mut next_used = self.next_used;413for (desc_chain, len) in desc_chains {414let desc_index = desc_chain.index();415debug_assert!(desc_index < self.size);416let id = Le32::from(u32::from(desc_index));417let len = Le32::from(len);418419let wrapped_index = usize::from(self.wrap_queue_index(next_used));420// SAFETY: `wrapped_index` is always in bounds due to `wrap_queue_index()`.421let elem_ptr = unsafe { elems_ptr.add(wrapped_index) };422423// SAFETY: `elem_ptr` is always a valid pointer424unsafe {425std::ptr::write_volatile(std::ptr::addr_of_mut!((*elem_ptr).id), id);426std::ptr::write_volatile(std::ptr::addr_of_mut!((*elem_ptr).len), len);427};428429next_used += Wrapping(1);430}431432if next_used != self.next_used {433fence(Ordering::Release);434used_index_atomic.store(next_used.0, Ordering::Relaxed);435self.next_used = next_used;436}437}438439/// Returns if the queue should have an interrupt sent based on its state.440///441/// This function implements `VIRTIO_RING_F_EVENT_IDX`, otherwise known as442/// interrupt suppression. The virtio spec provides the driver with a field,443/// `used_event`, which says that once we write that descriptor (or several444/// in the case of a flurry of `add_used` calls), we should send a445/// notification. Because the values involved wrap around `u16::MAX`, and to446/// avoid checking the condition on every `add_used` call, the math is a447/// little complicated.448///449/// The critical inequality is:450/// ```text451/// (next_used - 1) - used_event < next_used - last_used452/// ```453///454/// For illustration purposes, we label it as `A < B`, where455/// `A = (next_used -1) - used_event`, and `B = next_used - last_used`.456///457/// `A` and `B` represent two distances, measured in a wrapping ring of size458/// `u16::MAX`. In the "send intr" case, the inequality is true. In the459/// "don't send intr" case, the inequality is false. We must be very careful460/// in assigning a direction to the ring, so that when we461/// graph the subtraction operations, we are measuring the right distance462/// (similar to how DC circuits are analyzed).463///464/// The two distances are as follows:465/// * `A` is the distance between the driver's requested notification point, and the current466/// position in the ring.467///468/// * `B` is the distance between the last time we notified the guest, and the current position469/// in the ring.470///471/// If we graph these distances for the situation where we want to notify472/// the guest, and when we don't want to notify the guest, we see that473/// `A < B` becomes true the moment `next_used - 1` passes `used_event`. See474/// the graphs at the bottom of this comment block for a more visual475/// explanation.476///477/// Once an interrupt is sent, we have a final useful property: last_used478/// moves up next_used, which causes the inequality to be false. Thus, we479/// won't send notifications again until `used_event` is moved forward by480/// the driver.481///482/// Finally, let's talk about a couple of ways to write this inequality483/// that don't work, and critically, explain *why*.484///485/// First, a naive reading of the virtio spec might lead us to ask: why not486/// just use the following inequality:487/// ```text488/// next_used - 1 >= used_event489/// ```490///491/// because that's much simpler, right? The trouble is that the ring wraps,492/// so it could be that a smaller index is actually ahead of a larger one.493/// That's why we have to use distances in the ring instead.494///495/// Second, one might look at the correct inequality:496/// ```text497/// (next_used - 1) - used_event < next_used - last_used498/// ```499///500/// And try to simplify it to:501/// ```text502/// last_used - 1 < used_event503/// ```504///505/// Functionally, this won't work because next_used isn't present at all506/// anymore. (Notifications will never be sent.) But why is that? The algebra507/// here *appears* to work out, but all semantic meaning is lost. There are508/// two explanations for why this happens:509/// * The intuitive one: the terms in the inequality are not actually separable; in other words,510/// (next_used - last_used) is an inseparable term, so subtracting next_used from both sides511/// of the original inequality and zeroing them out is semantically invalid. But why aren't512/// they separable? See below.513/// * The theoretical one: canceling like terms relies a vector space law: a + x = b + x => a =514/// b (cancellation law). For congruences / equality under modulo, this law is satisfied, but515/// for inequalities under mod, it is not; therefore, we cannot cancel like terms.516///517/// ```text518/// ┌──────────────────────────────────┐519/// │ │520/// │ │521/// │ │522/// │ ┌──────────── next_used - 1523/// │ │A x524/// │ │ ┌────────────x────────────┐525/// │ │ │ x │526/// │ │ │ │527/// │ │ │ │ │528/// │ │ │ │ │529/// │ used_event xxxx + ◄───┘ xxxxx last_used530/// │ │ │ │531/// │ │ Send intr │ │532/// │ │ │ │533/// │ └─────────────────────────┘ │534/// │ │535/// │ B │536/// └────────────────────────────────────────────────────┘537///538/// ┌───────────────────────────────────────────────────┐539/// │ A │540/// │ ┌────────────────────────┐ │541/// │ │ │ │542/// │ │ │ │543/// │ │ │ │ │544/// │ │ │ │ │545/// used_event xxxx │ xxxxx last_used │546/// │ + ◄───┘ │ │ │547/// │ │ │ │548/// │ Don't send intr │ │ │549/// │ │ │ │550/// └───────────x────────────┘ │ │551/// x │ │552/// next_used - 1 │ │553/// │ │ B │ │554/// │ └────────────────────┘ │555/// │ │556/// └──────────────────────────────────┘557/// ```558fn queue_wants_interrupt(&self) -> bool {559if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {560let used_event = self.get_used_event();561self.next_used - used_event - Wrapping(1) < self.next_used - self.last_used562} else {563!self.get_avail_flag(VIRTQ_AVAIL_F_NO_INTERRUPT)564}565}566567/// inject interrupt into guest on this queue568/// return true: interrupt is injected into guest for this queue569/// false: interrupt isn't injected570pub fn trigger_interrupt(&mut self) -> bool {571if self.queue_wants_interrupt() {572self.last_used = self.next_used;573self.interrupt.signal_used_queue(self.vector);574true575} else {576false577}578}579580pub fn snapshot(&self) -> anyhow::Result<AnySnapshot> {581AnySnapshot::to_any(SplitQueueSnapshot {582size: self.size,583vector: self.vector,584desc_table: self.desc_table,585avail_ring: self.avail_ring,586used_ring: self.used_ring,587next_avail: self.next_avail,588next_used: self.next_used,589features: self.features,590last_used: self.last_used,591})592.context("failed to serialize MsixConfigSnapshot")593}594595pub fn restore(596queue_value: AnySnapshot,597mem: &GuestMemory,598event: Event,599interrupt: Interrupt,600) -> anyhow::Result<SplitQueue> {601let s: SplitQueueSnapshot = AnySnapshot::from_any(queue_value)?;602let queue = SplitQueue {603mem: mem.clone(),604event,605interrupt,606size: s.size,607vector: s.vector,608desc_table: s.desc_table,609avail_ring: s.avail_ring,610used_ring: s.used_ring,611next_avail: s.next_avail,612next_used: s.next_used,613features: s.features,614last_used: s.last_used,615};616Ok(queue)617}618}619620#[cfg(test)]621mod tests {622use std::convert::TryInto;623use std::mem::offset_of;624625use data_model::Le16;626use data_model::Le32;627use data_model::Le64;628use zerocopy::FromBytes;629use zerocopy::Immutable;630use zerocopy::IntoBytes;631use zerocopy::KnownLayout;632633use super::*;634use crate::virtio::create_descriptor_chain;635use crate::virtio::Desc;636use crate::virtio::Interrupt;637use crate::virtio::Queue;638639const GUEST_MEMORY_SIZE: u64 = 0x10000;640const DESC_OFFSET: u64 = 0;641const AVAIL_OFFSET: u64 = 0x200;642const USED_OFFSET: u64 = 0x400;643const QUEUE_SIZE: usize = 0x10;644const BUFFER_OFFSET: u64 = 0x8000;645const BUFFER_LEN: u32 = 0x400;646647#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]648#[repr(C)]649struct Avail {650flags: Le16,651idx: Le16,652ring: [Le16; QUEUE_SIZE],653used_event: Le16,654}655656impl Default for Avail {657fn default() -> Self {658Avail {659flags: Le16::from(0u16),660idx: Le16::from(0u16),661ring: [Le16::from(0u16); QUEUE_SIZE],662used_event: Le16::from(0u16),663}664}665}666667#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]668#[repr(C)]669struct UsedElem {670id: Le32,671len: Le32,672}673674impl Default for UsedElem {675fn default() -> Self {676UsedElem {677id: Le32::from(0u32),678len: Le32::from(0u32),679}680}681}682683#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]684#[repr(C, packed)]685struct Used {686flags: Le16,687idx: Le16,688used_elem_ring: [UsedElem; QUEUE_SIZE],689avail_event: Le16,690}691692impl Default for Used {693fn default() -> Self {694Used {695flags: Le16::from(0u16),696idx: Le16::from(0u16),697used_elem_ring: [UsedElem::default(); QUEUE_SIZE],698avail_event: Le16::from(0u16),699}700}701}702703fn setup_vq(queue: &mut QueueConfig, mem: &GuestMemory) -> Queue {704let desc = Desc {705addr: Le64::from(BUFFER_OFFSET),706len: Le32::from(BUFFER_LEN),707flags: Le16::from(0u16),708next: Le16::from(1u16),709};710let _ = mem.write_obj_at_addr(desc, GuestAddress(DESC_OFFSET));711712let avail = Avail::default();713let _ = mem.write_obj_at_addr(avail, GuestAddress(AVAIL_OFFSET));714715let used = Used::default();716let _ = mem.write_obj_at_addr(used, GuestAddress(USED_OFFSET));717718queue.set_desc_table(GuestAddress(DESC_OFFSET));719queue.set_avail_ring(GuestAddress(AVAIL_OFFSET));720queue.set_used_ring(GuestAddress(USED_OFFSET));721queue.ack_features((1u64) << VIRTIO_RING_F_EVENT_IDX);722queue.set_ready(true);723724queue725.activate(mem, Event::new().unwrap(), Interrupt::new_for_test())726.expect("QueueConfig::activate failed")727}728729fn fake_desc_chain(mem: &GuestMemory) -> DescriptorChain {730create_descriptor_chain(mem, GuestAddress(0), GuestAddress(0), Vec::new(), 0)731.expect("failed to create descriptor chain")732}733734#[test]735fn queue_event_id_guest_fast() {736let mut queue =737QueueConfig::new(QUEUE_SIZE.try_into().unwrap(), 1 << VIRTIO_RING_F_EVENT_IDX);738let memory_start_addr = GuestAddress(0x0);739let mem = GuestMemory::new(&[(memory_start_addr, GUEST_MEMORY_SIZE)]).unwrap();740let mut queue = setup_vq(&mut queue, &mem);741742// Offset of used_event within Avail structure743let used_event_offset = offset_of!(Avail, used_event) as u64;744let used_event_address = GuestAddress(AVAIL_OFFSET + used_event_offset);745746// Assume driver submit 0x100 req to device,747// device has handled them, so increase self.next_used to 0x100748let mut device_generate: Wrapping<u16> = Wrapping(0x100);749for _ in 0..device_generate.0 {750queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);751}752753// At this moment driver hasn't handled any interrupts yet, so it754// should inject interrupt.755assert_eq!(queue.trigger_interrupt(), true);756757// Driver handle all the interrupts and update avail.used_event to 0x100758let mut driver_handled = device_generate;759let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);760761// At this moment driver have handled all the interrupts, and762// device doesn't generate more data, so interrupt isn't needed.763assert_eq!(queue.trigger_interrupt(), false);764765// Assume driver submit another u16::MAX - 0x100 req to device,766// Device has handled all of them, so increase self.next_used to u16::MAX767for _ in device_generate.0..u16::MAX {768queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);769}770device_generate = Wrapping(u16::MAX);771772// At this moment driver just handled 0x100 interrupts, so it773// should inject interrupt.774assert_eq!(queue.trigger_interrupt(), true);775776// driver handle all the interrupts and update avail.used_event to u16::MAX777driver_handled = device_generate;778let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);779780// At this moment driver have handled all the interrupts, and781// device doesn't generate more data, so interrupt isn't needed.782assert_eq!(queue.trigger_interrupt(), false);783784// Assume driver submit another 1 request,785// device has handled it, so wrap self.next_used to 0786queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);787device_generate += Wrapping(1);788789// At this moment driver has handled all the previous interrupts, so it790// should inject interrupt again.791assert_eq!(queue.trigger_interrupt(), true);792793// driver handle that interrupts and update avail.used_event to 0794driver_handled = device_generate;795let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);796797// At this moment driver have handled all the interrupts, and798// device doesn't generate more data, so interrupt isn't needed.799assert_eq!(queue.trigger_interrupt(), false);800}801802#[test]803fn queue_event_id_guest_slow() {804let mut queue =805QueueConfig::new(QUEUE_SIZE.try_into().unwrap(), 1 << VIRTIO_RING_F_EVENT_IDX);806let memory_start_addr = GuestAddress(0x0);807let mem = GuestMemory::new(&[(memory_start_addr, GUEST_MEMORY_SIZE)]).unwrap();808let mut queue = setup_vq(&mut queue, &mem);809810// Offset of used_event within Avail structure811let used_event_offset = offset_of!(Avail, used_event) as u64;812let used_event_address = GuestAddress(AVAIL_OFFSET + used_event_offset);813814// Assume driver submit 0x100 req to device,815// device have handled 0x100 req, so increase self.next_used to 0x100816let mut device_generate: Wrapping<u16> = Wrapping(0x100);817for _ in 0..device_generate.0 {818queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);819}820821// At this moment driver hasn't handled any interrupts yet, so it822// should inject interrupt.823assert_eq!(queue.trigger_interrupt(), true);824825// Driver handle part of the interrupts and update avail.used_event to 0x80826let mut driver_handled = Wrapping(0x80);827let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);828829// At this moment driver hasn't finished last interrupt yet,830// so interrupt isn't needed.831assert_eq!(queue.trigger_interrupt(), false);832833// Assume driver submit another 1 request,834// device has handled it, so increment self.next_used.835queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);836device_generate += Wrapping(1);837838// At this moment driver hasn't finished last interrupt yet,839// so interrupt isn't needed.840assert_eq!(queue.trigger_interrupt(), false);841842// Assume driver submit another u16::MAX - 0x101 req to device,843// Device has handled all of them, so increase self.next_used to u16::MAX844for _ in device_generate.0..u16::MAX {845queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);846}847device_generate = Wrapping(u16::MAX);848849// At this moment driver hasn't finished last interrupt yet,850// so interrupt isn't needed.851assert_eq!(queue.trigger_interrupt(), false);852853// driver handle most of the interrupts and update avail.used_event to u16::MAX - 1,854driver_handled = device_generate - Wrapping(1);855let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);856857// Assume driver submit another 1 request,858// device has handled it, so wrap self.next_used to 0859queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);860device_generate += Wrapping(1);861862// At this moment driver has already finished the last interrupt(0x100),863// and device service other request, so new interrupt is needed.864assert_eq!(queue.trigger_interrupt(), true);865866// Assume driver submit another 1 request,867// device has handled it, so increment self.next_used to 1868queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);869device_generate += Wrapping(1);870871// At this moment driver hasn't finished last interrupt((Wrapping(0)) yet,872// so interrupt isn't needed.873assert_eq!(queue.trigger_interrupt(), false);874875// driver handle all the remain interrupts and wrap avail.used_event to 0x1.876driver_handled = device_generate;877let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);878879// At this moment driver has handled all the interrupts, and880// device doesn't generate more data, so interrupt isn't needed.881assert_eq!(queue.trigger_interrupt(), false);882883// Assume driver submit another 1 request,884// device has handled it, so increase self.next_used.885queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);886device_generate += Wrapping(1);887888// At this moment driver has finished all the previous interrupts, so it889// should inject interrupt again.890assert_eq!(queue.trigger_interrupt(), true);891}892}893894895