| // Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| // Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved. |
| // Copyright © 2019 Intel Corporation. |
| // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE-BSD-3-Clause file. |
| // |
| // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause |
| |
| use std::mem::size_of; |
| use std::num::Wrapping; |
| use std::ops::Deref; |
| use std::sync::atomic::{fence, Ordering}; |
| |
| use vm_memory::{Address, Bytes, GuestAddress, GuestMemory}; |
| |
| use crate::defs::{ |
| DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR, |
| VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE, VIRTQ_AVAIL_RING_META_SIZE, |
| VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_RING_HEADER_SIZE, VIRTQ_USED_RING_META_SIZE, |
| }; |
| use crate::{ |
| error, Descriptor, DescriptorChain, Error, QueueGuard, QueueOwnedT, QueueState, QueueT, |
| VirtqUsedElem, |
| }; |
| use virtio_bindings::bindings::virtio_ring::VRING_USED_F_NO_NOTIFY; |
| |
| /// The maximum queue size as defined in the Virtio Spec. |
| pub const MAX_QUEUE_SIZE: u16 = 32768; |
| |
| /// Struct to maintain information and manipulate a virtio queue. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use virtio_queue::{Queue, QueueOwnedT, QueueT}; |
| /// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap}; |
| /// |
| /// let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| /// let mut queue = Queue::new(1024).unwrap(); |
| /// |
| /// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO). |
| /// queue.set_size(8); |
| /// queue.set_desc_table_address(Some(0x1000), None); |
| /// queue.set_avail_ring_address(Some(0x2000), None); |
| /// queue.set_used_ring_address(Some(0x3000), None); |
| /// queue.set_event_idx(true); |
| /// queue.set_ready(true); |
| /// // The user should check if the queue is valid before starting to use it. |
| /// assert!(queue.is_valid(&m)); |
| /// |
| /// // Here the driver would add entries in the available ring and then update the `idx` field of |
| /// // the available ring (address = 0x2000 + 2). |
| /// m.write_obj(3, GuestAddress(0x2002)); |
| /// |
| /// loop { |
| /// queue.disable_notification(&m).unwrap(); |
| /// |
| /// // Consume entries from the available ring. |
| /// while let Some(chain) = queue.iter(&m).unwrap().next() { |
| /// // Process the descriptor chain, and then add an entry in the used ring and optionally |
| /// // notify the driver. |
| /// queue.add_used(&m, chain.head_index(), 0x100).unwrap(); |
| /// |
| /// if queue.needs_notification(&m).unwrap() { |
| /// // Here we would notify the driver it has new entries in the used ring to consume. |
| /// } |
| /// } |
| /// if !queue.enable_notification(&m).unwrap() { |
| /// break; |
| /// } |
| /// } |
| /// |
| /// // We can reset the queue at some point. |
| /// queue.reset(); |
| /// // The queue should not be ready after reset. |
| /// assert!(!queue.ready()); |
| /// ``` |
| #[derive(Debug, Default, PartialEq, Eq)] |
| pub struct Queue { |
| /// The maximum size in elements offered by the device. |
| max_size: u16, |
| |
| /// Tail position of the available ring. |
| next_avail: Wrapping<u16>, |
| |
| /// Head position of the used ring. |
| next_used: Wrapping<u16>, |
| |
| /// VIRTIO_F_RING_EVENT_IDX negotiated. |
| event_idx_enabled: bool, |
| |
| /// The number of descriptor chains placed in the used ring via `add_used` |
| /// since the last time `needs_notification` was called on the associated queue. |
| num_added: Wrapping<u16>, |
| |
| /// The queue size in elements the driver selected. |
| size: u16, |
| |
| /// Indicates if the queue is finished with configuration. |
| ready: bool, |
| |
| /// Guest physical address of the descriptor table. |
| desc_table: GuestAddress, |
| |
| /// Guest physical address of the available ring. |
| avail_ring: GuestAddress, |
| |
| /// Guest physical address of the used ring. |
| used_ring: GuestAddress, |
| } |
| |
| impl Queue { |
| /// Equivalent of [`QueueT::set_size`] returning an error in case of invalid size. |
| /// |
| /// This should not be directly used, as the preferred method is part of the [`QueueT`] |
| /// interface. This is a convenience function for implementing save/restore capabilities. |
| pub fn try_set_size(&mut self, size: u16) -> Result<(), Error> { |
| if size > self.max_size() || size == 0 || (size & (size - 1)) != 0 { |
| return Err(Error::InvalidSize); |
| } |
| self.size = size; |
| Ok(()) |
| } |
| |
| /// Tries to set the descriptor table address. In case of an invalid value, the address is |
| /// not updated. |
| /// |
| /// This should not be directly used, as the preferred method is |
| /// [`QueueT::set_desc_table_address`]. This is a convenience function for implementing |
| /// save/restore capabilities. |
| pub fn try_set_desc_table_address(&mut self, desc_table: GuestAddress) -> Result<(), Error> { |
| if desc_table.mask(0xf) != 0 { |
| return Err(Error::InvalidDescTableAlign); |
| } |
| self.desc_table = desc_table; |
| |
| Ok(()) |
| } |
| |
| /// Tries to update the available ring address. In case of an invalid value, the address is |
| /// not updated. |
| /// |
| /// This should not be directly used, as the preferred method is |
| /// [`QueueT::set_avail_ring_address`]. This is a convenience function for implementing |
| /// save/restore capabilities. |
| pub fn try_set_avail_ring_address(&mut self, avail_ring: GuestAddress) -> Result<(), Error> { |
| if avail_ring.mask(0x1) != 0 { |
| return Err(Error::InvalidAvailRingAlign); |
| } |
| self.avail_ring = avail_ring; |
| Ok(()) |
| } |
| |
| /// Tries to update the used ring address. In cae of an invalid value, the address is not |
| /// updated. |
| /// |
| /// This should not be directly used, as the preferred method is |
| /// [`QueueT::set_used_ring_address`]. This is a convenience function for implementing |
| /// save/restore capabilities. |
| pub fn try_set_used_ring_address(&mut self, used_ring: GuestAddress) -> Result<(), Error> { |
| if used_ring.mask(0x3) != 0 { |
| return Err(Error::InvalidUsedRingAlign); |
| } |
| self.used_ring = used_ring; |
| Ok(()) |
| } |
| |
| /// Returns the state of the `Queue`. |
| /// |
| /// This is useful for implementing save/restore capabilities. |
| /// The state does not have support for serialization, but this can be |
| /// added by VMMs locally through the use of a |
| /// [remote type](https://serde.rs/remote-derive.html). |
| /// |
| /// Alternatively, a version aware and serializable/deserializable QueueState |
| /// is available in the `virtio-queue-ser` crate. |
| pub fn state(&self) -> QueueState { |
| QueueState { |
| max_size: self.max_size, |
| next_avail: self.next_avail(), |
| next_used: self.next_used(), |
| event_idx_enabled: self.event_idx_enabled, |
| size: self.size, |
| ready: self.ready, |
| desc_table: self.desc_table(), |
| avail_ring: self.avail_ring(), |
| used_ring: self.used_ring(), |
| } |
| } |
| |
| // Helper method that writes `val` to the `avail_event` field of the used ring, using |
| // the provided ordering. |
| fn set_avail_event<M: GuestMemory>( |
| &self, |
| mem: &M, |
| val: u16, |
| order: Ordering, |
| ) -> Result<(), Error> { |
| // This can not overflow an u64 since it is working with relatively small numbers compared |
| // to u64::MAX. |
| let avail_event_offset = |
| VIRTQ_USED_RING_HEADER_SIZE + VIRTQ_USED_ELEMENT_SIZE * u64::from(self.size); |
| let addr = self |
| .used_ring |
| .checked_add(avail_event_offset) |
| .ok_or(Error::AddressOverflow)?; |
| |
| mem.store(u16::to_le(val), addr, order) |
| .map_err(Error::GuestMemory) |
| } |
| |
| // Set the value of the `flags` field of the used ring, applying the specified ordering. |
| fn set_used_flags<M: GuestMemory>( |
| &mut self, |
| mem: &M, |
| val: u16, |
| order: Ordering, |
| ) -> Result<(), Error> { |
| mem.store(u16::to_le(val), self.used_ring, order) |
| .map_err(Error::GuestMemory) |
| } |
| |
| // Write the appropriate values to enable or disable notifications from the driver. |
| // |
| // Every access in this method uses `Relaxed` ordering because a fence is added by the caller |
| // when appropriate. |
| fn set_notification<M: GuestMemory>(&mut self, mem: &M, enable: bool) -> Result<(), Error> { |
| if enable { |
| if self.event_idx_enabled { |
| // We call `set_avail_event` using the `next_avail` value, instead of reading |
| // and using the current `avail_idx` to avoid missing notifications. More |
| // details in `enable_notification`. |
| self.set_avail_event(mem, self.next_avail.0, Ordering::Relaxed) |
| } else { |
| self.set_used_flags(mem, 0, Ordering::Relaxed) |
| } |
| } else if !self.event_idx_enabled { |
| self.set_used_flags(mem, VRING_USED_F_NO_NOTIFY as u16, Ordering::Relaxed) |
| } else { |
| // Notifications are effectively disabled by default after triggering once when |
| // `VIRTIO_F_EVENT_IDX` is negotiated, so we don't do anything in that case. |
| Ok(()) |
| } |
| } |
| |
| // Return the value present in the used_event field of the avail ring. |
| // |
| // If the VIRTIO_F_EVENT_IDX feature bit is not negotiated, the flags field in the available |
| // ring offers a crude mechanism for the driver to inform the device that it doesn’t want |
| // interrupts when buffers are used. Otherwise virtq_avail.used_event is a more performant |
| // alternative where the driver specifies how far the device can progress before interrupting. |
| // |
| // Neither of these interrupt suppression methods are reliable, as they are not synchronized |
| // with the device, but they serve as useful optimizations. So we only ensure access to the |
| // virtq_avail.used_event is atomic, but do not need to synchronize with other memory accesses. |
| fn used_event<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> { |
| // This can not overflow an u64 since it is working with relatively small numbers compared |
| // to u64::MAX. |
| let used_event_offset = |
| VIRTQ_AVAIL_RING_HEADER_SIZE + u64::from(self.size) * VIRTQ_AVAIL_ELEMENT_SIZE; |
| let used_event_addr = self |
| .avail_ring |
| .checked_add(used_event_offset) |
| .ok_or(Error::AddressOverflow)?; |
| |
| mem.load(used_event_addr, order) |
| .map(u16::from_le) |
| .map(Wrapping) |
| .map_err(Error::GuestMemory) |
| } |
| } |
| |
| impl<'a> QueueGuard<'a> for Queue { |
| type G = &'a mut Self; |
| } |
| |
| impl QueueT for Queue { |
| fn new(max_size: u16) -> Result<Self, Error> { |
| // We need to check that the max size is a power of 2 because we're setting this as the |
| // queue size, and the valid queue sizes are a power of 2 as per the specification. |
| if max_size == 0 || max_size > MAX_QUEUE_SIZE || (max_size & (max_size - 1)) != 0 { |
| return Err(Error::InvalidMaxSize); |
| } |
| Ok(Queue { |
| max_size, |
| size: max_size, |
| ready: false, |
| desc_table: GuestAddress(DEFAULT_DESC_TABLE_ADDR), |
| avail_ring: GuestAddress(DEFAULT_AVAIL_RING_ADDR), |
| used_ring: GuestAddress(DEFAULT_USED_RING_ADDR), |
| next_avail: Wrapping(0), |
| next_used: Wrapping(0), |
| event_idx_enabled: false, |
| num_added: Wrapping(0), |
| }) |
| } |
| |
| fn is_valid<M: GuestMemory>(&self, mem: &M) -> bool { |
| let queue_size = self.size as u64; |
| let desc_table = self.desc_table; |
| // The multiplication can not overflow an u64 since we are multiplying an u16 with a |
| // small number. |
| let desc_table_size = size_of::<Descriptor>() as u64 * queue_size; |
| let avail_ring = self.avail_ring; |
| // The operations below can not overflow an u64 since they're working with relatively small |
| // numbers compared to u64::MAX. |
| let avail_ring_size = VIRTQ_AVAIL_RING_META_SIZE + VIRTQ_AVAIL_ELEMENT_SIZE * queue_size; |
| let used_ring = self.used_ring; |
| let used_ring_size = VIRTQ_USED_RING_META_SIZE + VIRTQ_USED_ELEMENT_SIZE * queue_size; |
| |
| if !self.ready { |
| error!("attempt to use virtio queue that is not marked ready"); |
| false |
| } else if desc_table |
| .checked_add(desc_table_size) |
| .map_or(true, |v| !mem.address_in_range(v)) |
| { |
| error!( |
| "virtio queue descriptor table goes out of bounds: start:0x{:08x} size:0x{:08x}", |
| desc_table.raw_value(), |
| desc_table_size |
| ); |
| false |
| } else if avail_ring |
| .checked_add(avail_ring_size) |
| .map_or(true, |v| !mem.address_in_range(v)) |
| { |
| error!( |
| "virtio queue available ring goes out of bounds: start:0x{:08x} size:0x{:08x}", |
| avail_ring.raw_value(), |
| avail_ring_size |
| ); |
| false |
| } else if used_ring |
| .checked_add(used_ring_size) |
| .map_or(true, |v| !mem.address_in_range(v)) |
| { |
| error!( |
| "virtio queue used ring goes out of bounds: start:0x{:08x} size:0x{:08x}", |
| used_ring.raw_value(), |
| used_ring_size |
| ); |
| false |
| } else { |
| true |
| } |
| } |
| |
| fn reset(&mut self) { |
| self.ready = false; |
| self.size = self.max_size; |
| self.desc_table = GuestAddress(DEFAULT_DESC_TABLE_ADDR); |
| self.avail_ring = GuestAddress(DEFAULT_AVAIL_RING_ADDR); |
| self.used_ring = GuestAddress(DEFAULT_USED_RING_ADDR); |
| self.next_avail = Wrapping(0); |
| self.next_used = Wrapping(0); |
| self.num_added = Wrapping(0); |
| self.event_idx_enabled = false; |
| } |
| |
| fn lock(&mut self) -> <Self as QueueGuard>::G { |
| self |
| } |
| |
| fn max_size(&self) -> u16 { |
| self.max_size |
| } |
| |
| fn size(&self) -> u16 { |
| self.size |
| } |
| |
| fn set_size(&mut self, size: u16) { |
| if self.try_set_size(size).is_err() { |
| error!("virtio queue with invalid size: {}", size); |
| } |
| } |
| |
| fn ready(&self) -> bool { |
| self.ready |
| } |
| |
| fn set_ready(&mut self, ready: bool) { |
| self.ready = ready; |
| } |
| |
| fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) { |
| let low = low.unwrap_or(self.desc_table.0 as u32) as u64; |
| let high = high.unwrap_or((self.desc_table.0 >> 32) as u32) as u64; |
| |
| let desc_table = GuestAddress((high << 32) | low); |
| if self.try_set_desc_table_address(desc_table).is_err() { |
| error!("virtio queue descriptor table breaks alignment constraints"); |
| } |
| } |
| |
| fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) { |
| let low = low.unwrap_or(self.avail_ring.0 as u32) as u64; |
| let high = high.unwrap_or((self.avail_ring.0 >> 32) as u32) as u64; |
| |
| let avail_ring = GuestAddress((high << 32) | low); |
| if self.try_set_avail_ring_address(avail_ring).is_err() { |
| error!("virtio queue available ring breaks alignment constraints"); |
| } |
| } |
| |
| fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) { |
| let low = low.unwrap_or(self.used_ring.0 as u32) as u64; |
| let high = high.unwrap_or((self.used_ring.0 >> 32) as u32) as u64; |
| |
| let used_ring = GuestAddress((high << 32) | low); |
| if self.try_set_used_ring_address(used_ring).is_err() { |
| error!("virtio queue used ring breaks alignment constraints"); |
| } |
| } |
| |
| fn set_event_idx(&mut self, enabled: bool) { |
| self.event_idx_enabled = enabled; |
| } |
| |
| fn avail_idx<M>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> |
| where |
| M: GuestMemory + ?Sized, |
| { |
| let addr = self |
| .avail_ring |
| .checked_add(2) |
| .ok_or(Error::AddressOverflow)?; |
| |
| mem.load(addr, order) |
| .map(u16::from_le) |
| .map(Wrapping) |
| .map_err(Error::GuestMemory) |
| } |
| |
| fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> { |
| let addr = self |
| .used_ring |
| .checked_add(2) |
| .ok_or(Error::AddressOverflow)?; |
| |
| mem.load(addr, order) |
| .map(u16::from_le) |
| .map(Wrapping) |
| .map_err(Error::GuestMemory) |
| } |
| |
| fn add_used<M: GuestMemory>( |
| &mut self, |
| mem: &M, |
| head_index: u16, |
| len: u32, |
| ) -> Result<(), Error> { |
| if head_index >= self.size { |
| error!( |
| "attempted to add out of bounds descriptor to used ring: {}", |
| head_index |
| ); |
| return Err(Error::InvalidDescriptorIndex); |
| } |
| |
| let next_used_index = u64::from(self.next_used.0 % self.size); |
| // This can not overflow an u64 since it is working with relatively small numbers compared |
| // to u64::MAX. |
| let offset = VIRTQ_USED_RING_HEADER_SIZE + next_used_index * VIRTQ_USED_ELEMENT_SIZE; |
| let addr = self |
| .used_ring |
| .checked_add(offset) |
| .ok_or(Error::AddressOverflow)?; |
| mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr) |
| .map_err(Error::GuestMemory)?; |
| |
| self.next_used += Wrapping(1); |
| self.num_added += Wrapping(1); |
| |
| mem.store( |
| u16::to_le(self.next_used.0), |
| self.used_ring |
| .checked_add(2) |
| .ok_or(Error::AddressOverflow)?, |
| Ordering::Release, |
| ) |
| .map_err(Error::GuestMemory) |
| } |
| |
| // TODO: Turn this into a doc comment/example. |
| // With the current implementation, a common way of consuming entries from the available ring |
| // while also leveraging notification suppression is to use a loop, for example: |
| // |
| // loop { |
| // // We have to explicitly disable notifications if `VIRTIO_F_EVENT_IDX` has not been |
| // // negotiated. |
| // self.disable_notification()?; |
| // |
| // for chain in self.iter()? { |
| // // Do something with each chain ... |
| // // Let's assume we process all available chains here. |
| // } |
| // |
| // // If `enable_notification` returns `true`, the driver has added more entries to the |
| // // available ring. |
| // if !self.enable_notification()? { |
| // break; |
| // } |
| // } |
| fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> { |
| self.set_notification(mem, true)?; |
| // Ensures the following read is not reordered before any previous write operation. |
| fence(Ordering::SeqCst); |
| |
| // We double check here to avoid the situation where the available ring has been updated |
| // just before we re-enabled notifications, and it's possible to miss one. We compare the |
| // current `avail_idx` value to `self.next_avail` because it's where we stopped processing |
| // entries. There are situations where we intentionally avoid processing everything in the |
| // available ring (which will cause this method to return `true`), but in that case we'll |
| // probably not re-enable notifications as we already know there are pending entries. |
| self.avail_idx(mem, Ordering::Relaxed) |
| .map(|idx| idx != self.next_avail) |
| } |
| |
| fn disable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<(), Error> { |
| self.set_notification(mem, false) |
| } |
| |
| fn needs_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> { |
| let used_idx = self.next_used; |
| |
| // Complete all the writes in add_used() before reading the event. |
| fence(Ordering::SeqCst); |
| |
| // The VRING_AVAIL_F_NO_INTERRUPT flag isn't supported yet. |
| |
| // When the `EVENT_IDX` feature is negotiated, the driver writes into `used_event` |
| // a value that's used by the device to determine whether a notification must |
| // be submitted after adding a descriptor chain to the used ring. According to the |
| // standard, the notification must be sent when `next_used == used_event + 1`, but |
| // various device model implementations rely on an inequality instead, most likely |
| // to also support use cases where a bunch of descriptor chains are added to the used |
| // ring first, and only afterwards the `needs_notification` logic is called. For example, |
| // the approach based on `num_added` below is taken from the Linux Kernel implementation |
| // (i.e. https://elixir.bootlin.com/linux/v5.15.35/source/drivers/virtio/virtio_ring.c#L661) |
| |
| // The `old` variable below is used to determine the value of `next_used` from when |
| // `needs_notification` was called last (each `needs_notification` call resets `num_added` |
| // to zero, while each `add_used` called increments it by one). Then, the logic below |
| // uses wrapped arithmetic to see whether `used_event` can be found between `old` and |
| // `next_used` in the circular sequence space of the used ring. |
| if self.event_idx_enabled { |
| let used_event = self.used_event(mem, Ordering::Relaxed)?; |
| let old = used_idx - self.num_added; |
| self.num_added = Wrapping(0); |
| |
| return Ok(used_idx - used_event - Wrapping(1) < used_idx - old); |
| } |
| |
| Ok(true) |
| } |
| |
| fn next_avail(&self) -> u16 { |
| self.next_avail.0 |
| } |
| |
| fn set_next_avail(&mut self, next_avail: u16) { |
| self.next_avail = Wrapping(next_avail); |
| } |
| |
| fn next_used(&self) -> u16 { |
| self.next_used.0 |
| } |
| |
| fn set_next_used(&mut self, next_used: u16) { |
| self.next_used = Wrapping(next_used); |
| } |
| |
| fn desc_table(&self) -> u64 { |
| self.desc_table.0 |
| } |
| |
| fn avail_ring(&self) -> u64 { |
| self.avail_ring.0 |
| } |
| |
| fn used_ring(&self) -> u64 { |
| self.used_ring.0 |
| } |
| |
| fn event_idx_enabled(&self) -> bool { |
| self.event_idx_enabled |
| } |
| |
| fn pop_descriptor_chain<M>(&mut self, mem: M) -> Option<DescriptorChain<M>> |
| where |
| M: Clone + Deref, |
| M::Target: GuestMemory, |
| { |
| // Default, iter-based impl. Will be subsequently improved. |
| match self.iter(mem) { |
| Ok(mut iter) => iter.next(), |
| Err(e) => { |
| error!("Iterator error {}", e); |
| None |
| } |
| } |
| } |
| } |
| |
| impl QueueOwnedT for Queue { |
| fn iter<M>(&mut self, mem: M) -> Result<AvailIter<'_, M>, Error> |
| where |
| M: Deref, |
| M::Target: GuestMemory, |
| { |
| // We're checking here that a reset did not happen without re-initializing the queue. |
| // TODO: In the future we might want to also check that the other parameters in the |
| // queue are valid. |
| if !self.ready || self.avail_ring == GuestAddress(0) { |
| return Err(Error::QueueNotReady); |
| } |
| |
| self.avail_idx(mem.deref(), Ordering::Acquire) |
| .map(move |idx| AvailIter::new(mem, idx, self))? |
| } |
| |
| fn go_to_previous_position(&mut self) { |
| self.next_avail -= Wrapping(1); |
| } |
| } |
| |
| /// Consuming iterator over all available descriptor chain heads in the queue. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// # use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; |
| /// # use virtio_queue::mock::MockSplitQueue; |
| /// use virtio_queue::{Descriptor, Queue, QueueOwnedT}; |
| /// use vm_memory::{GuestAddress, GuestMemoryMmap}; |
| /// |
| /// # fn populate_queue(m: &GuestMemoryMmap) -> Queue { |
| /// # let vq = MockSplitQueue::new(m, 16); |
| /// # let mut q: Queue = vq.create_queue().unwrap(); |
| /// # |
| /// # // The chains are (0, 1), (2, 3, 4) and (5, 6). |
| /// # let mut descs = Vec::new(); |
| /// # for i in 0..7 { |
| /// # let flags = match i { |
| /// # 1 | 6 => 0, |
| /// # 2 | 5 => VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, |
| /// # 4 => VRING_DESC_F_WRITE, |
| /// # _ => VRING_DESC_F_NEXT, |
| /// # }; |
| /// # |
| /// # descs.push(Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1)); |
| /// # } |
| /// # |
| /// # vq.add_desc_chains(&descs, 0).unwrap(); |
| /// # q |
| /// # } |
| /// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| /// // Populate the queue with descriptor chains and update the available ring accordingly. |
| /// let mut queue = populate_queue(m); |
| /// let mut i = queue.iter(m).unwrap(); |
| /// |
| /// { |
| /// let mut c = i.next().unwrap(); |
| /// let _first_head_index = c.head_index(); |
| /// // We should have two descriptors in the first chain. |
| /// let _desc1 = c.next().unwrap(); |
| /// let _desc2 = c.next().unwrap(); |
| /// } |
| /// |
| /// { |
| /// let c = i.next().unwrap(); |
| /// let _second_head_index = c.head_index(); |
| /// |
| /// let mut iter = c.writable(); |
| /// // We should have two writable descriptors in the second chain. |
| /// let _desc1 = iter.next().unwrap(); |
| /// let _desc2 = iter.next().unwrap(); |
| /// } |
| /// |
| /// { |
| /// let c = i.next().unwrap(); |
| /// let _third_head_index = c.head_index(); |
| /// |
| /// let mut iter = c.readable(); |
| /// // We should have one readable descriptor in the third chain. |
| /// let _desc1 = iter.next().unwrap(); |
| /// } |
| /// // Let's go back one position in the available ring. |
| /// i.go_to_previous_position(); |
| /// // We should be able to access again the third descriptor chain. |
| /// let c = i.next().unwrap(); |
| /// let _third_head_index = c.head_index(); |
| /// ``` |
| #[derive(Debug)] |
| pub struct AvailIter<'b, M> { |
| mem: M, |
| desc_table: GuestAddress, |
| avail_ring: GuestAddress, |
| queue_size: u16, |
| last_index: Wrapping<u16>, |
| next_avail: &'b mut Wrapping<u16>, |
| } |
| |
| impl<'b, M> AvailIter<'b, M> |
| where |
| M: Deref, |
| M::Target: GuestMemory, |
| { |
| /// Create a new instance of `AvailInter`. |
| /// |
| /// # Arguments |
| /// * `mem` - the `GuestMemory` object that can be used to access the queue buffers. |
| /// * `idx` - the index of the available ring entry where the driver would put the next |
| /// available descriptor chain. |
| /// * `queue` - the `Queue` object from which the needed data to create the `AvailIter` can |
| /// be retrieved. |
| pub(crate) fn new(mem: M, idx: Wrapping<u16>, queue: &'b mut Queue) -> Result<Self, Error> { |
| // The number of descriptor chain heads to process should always |
| // be smaller or equal to the queue size, as the driver should |
| // never ask the VMM to process a available ring entry more than |
| // once. Checking and reporting such incorrect driver behavior |
| // can prevent potential hanging and Denial-of-Service from |
| // happening on the VMM side. |
| if (idx - queue.next_avail).0 > queue.size { |
| return Err(Error::InvalidAvailRingIndex); |
| } |
| |
| Ok(AvailIter { |
| mem, |
| desc_table: queue.desc_table, |
| avail_ring: queue.avail_ring, |
| queue_size: queue.size, |
| last_index: idx, |
| next_avail: &mut queue.next_avail, |
| }) |
| } |
| |
| /// Goes back one position in the available descriptor chain offered by the driver. |
| /// |
| /// Rust does not support bidirectional iterators. This is the only way to revert the effect |
| /// of an iterator increment on the queue. |
| /// |
| /// Note: this method assumes there's only one thread manipulating the queue, so it should only |
| /// be invoked in single-threaded context. |
| pub fn go_to_previous_position(&mut self) { |
| *self.next_avail -= Wrapping(1); |
| } |
| } |
| |
| impl<'b, M> Iterator for AvailIter<'b, M> |
| where |
| M: Clone + Deref, |
| M::Target: GuestMemory, |
| { |
| type Item = DescriptorChain<M>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if *self.next_avail == self.last_index { |
| return None; |
| } |
| |
| // These two operations can not overflow an u64 since they're working with relatively small |
| // numbers compared to u64::MAX. |
| let elem_off = |
| u64::from(self.next_avail.0.checked_rem(self.queue_size)?) * VIRTQ_AVAIL_ELEMENT_SIZE; |
| let offset = VIRTQ_AVAIL_RING_HEADER_SIZE + elem_off; |
| |
| let addr = self.avail_ring.checked_add(offset)?; |
| let head_index: u16 = self |
| .mem |
| .load(addr, Ordering::Acquire) |
| .map(u16::from_le) |
| .map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value())) |
| .ok()?; |
| |
| *self.next_avail += Wrapping(1); |
| |
| Some(DescriptorChain::new( |
| self.mem.clone(), |
| self.desc_table, |
| self.queue_size, |
| head_index, |
| )) |
| } |
| } |
| |
| #[cfg(any(test, feature = "test-utils"))] |
| // It is convenient for tests to implement `PartialEq`, but it is not a |
| // proper implementation as `GuestMemory` errors cannot implement `PartialEq`. |
| impl PartialEq for Error { |
| fn eq(&self, other: &Self) -> bool { |
| format!("{}", &self) == format!("{}", other) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::defs::{DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR}; |
| use crate::mock::MockSplitQueue; |
| use crate::Descriptor; |
| use virtio_bindings::bindings::virtio_ring::{ |
| VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, VRING_USED_F_NO_NOTIFY, |
| }; |
| |
| use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap}; |
| |
| #[test] |
| fn test_queue_is_valid() { |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 16); |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // q is currently valid |
| assert!(q.is_valid(m)); |
| |
| // shouldn't be valid when not marked as ready |
| q.set_ready(false); |
| assert!(!q.ready()); |
| assert!(!q.is_valid(m)); |
| q.set_ready(true); |
| |
| // shouldn't be allowed to set a size > max_size |
| q.set_size(q.max_size() << 1); |
| assert_eq!(q.size, q.max_size()); |
| |
| // or set the size to 0 |
| q.set_size(0); |
| assert_eq!(q.size, q.max_size()); |
| |
| // or set a size which is not a power of 2 |
| q.set_size(11); |
| assert_eq!(q.size, q.max_size()); |
| |
| // but should be allowed to set a size if 0 < size <= max_size and size is a power of two |
| q.set_size(4); |
| assert_eq!(q.size, 4); |
| q.size = q.max_size(); |
| |
| // shouldn't be allowed to set an address that breaks the alignment constraint |
| q.set_desc_table_address(Some(0xf), None); |
| assert_eq!(q.desc_table.0, vq.desc_table_addr().0); |
| // should be allowed to set an aligned out of bounds address |
| q.set_desc_table_address(Some(0xffff_fff0), None); |
| assert_eq!(q.desc_table.0, 0xffff_fff0); |
| // but shouldn't be valid |
| assert!(!q.is_valid(m)); |
| // but should be allowed to set a valid description table address |
| q.set_desc_table_address(Some(0x10), None); |
| assert_eq!(q.desc_table.0, 0x10); |
| assert!(q.is_valid(m)); |
| let addr = vq.desc_table_addr().0; |
| q.set_desc_table_address(Some(addr as u32), Some((addr >> 32) as u32)); |
| |
| // shouldn't be allowed to set an address that breaks the alignment constraint |
| q.set_avail_ring_address(Some(0x1), None); |
| assert_eq!(q.avail_ring.0, vq.avail_addr().0); |
| // should be allowed to set an aligned out of bounds address |
| q.set_avail_ring_address(Some(0xffff_fffe), None); |
| assert_eq!(q.avail_ring.0, 0xffff_fffe); |
| // but shouldn't be valid |
| assert!(!q.is_valid(m)); |
| // but should be allowed to set a valid available ring address |
| q.set_avail_ring_address(Some(0x2), None); |
| assert_eq!(q.avail_ring.0, 0x2); |
| assert!(q.is_valid(m)); |
| let addr = vq.avail_addr().0; |
| q.set_avail_ring_address(Some(addr as u32), Some((addr >> 32) as u32)); |
| |
| // shouldn't be allowed to set an address that breaks the alignment constraint |
| q.set_used_ring_address(Some(0x3), None); |
| assert_eq!(q.used_ring.0, vq.used_addr().0); |
| // should be allowed to set an aligned out of bounds address |
| q.set_used_ring_address(Some(0xffff_fffc), None); |
| assert_eq!(q.used_ring.0, 0xffff_fffc); |
| // but shouldn't be valid |
| assert!(!q.is_valid(m)); |
| // but should be allowed to set a valid used ring address |
| q.set_used_ring_address(Some(0x4), None); |
| assert_eq!(q.used_ring.0, 0x4); |
| let addr = vq.used_addr().0; |
| q.set_used_ring_address(Some(addr as u32), Some((addr >> 32) as u32)); |
| assert!(q.is_valid(m)); |
| } |
| |
| #[test] |
| fn test_add_used() { |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(mem, 16); |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(0)); |
| assert_eq!(u16::from_le(vq.used().idx().load()), 0); |
| |
| // index too large |
| assert!(q.add_used(mem, 16, 0x1000).is_err()); |
| assert_eq!(u16::from_le(vq.used().idx().load()), 0); |
| |
| // should be ok |
| q.add_used(mem, 1, 0x1000).unwrap(); |
| assert_eq!(q.next_used, Wrapping(1)); |
| assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(1)); |
| assert_eq!(u16::from_le(vq.used().idx().load()), 1); |
| |
| let x = vq.used().ring().ref_at(0).unwrap().load(); |
| assert_eq!(x.id(), 1); |
| assert_eq!(x.len(), 0x1000); |
| } |
| |
| #[test] |
| fn test_reset_queue() { |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 16); |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| q.set_size(8); |
| // The address set by `MockSplitQueue` for the descriptor table is DEFAULT_DESC_TABLE_ADDR, |
| // so let's change it for testing the reset. |
| q.set_desc_table_address(Some(0x5000), None); |
| // Same for `event_idx_enabled`, `next_avail` `next_used` and `signalled_used`. |
| q.set_event_idx(true); |
| q.set_next_avail(2); |
| q.set_next_used(4); |
| q.num_added = Wrapping(15); |
| assert_eq!(q.size, 8); |
| // `create_queue` also marks the queue as ready. |
| assert!(q.ready); |
| assert_ne!(q.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR)); |
| assert_ne!(q.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR)); |
| assert_ne!(q.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR)); |
| assert_ne!(q.next_avail, Wrapping(0)); |
| assert_ne!(q.next_used, Wrapping(0)); |
| assert_ne!(q.num_added, Wrapping(0)); |
| assert!(q.event_idx_enabled); |
| |
| q.reset(); |
| assert_eq!(q.size, 16); |
| assert!(!q.ready); |
| assert_eq!(q.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR)); |
| assert_eq!(q.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR)); |
| assert_eq!(q.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR)); |
| assert_eq!(q.next_avail, Wrapping(0)); |
| assert_eq!(q.next_used, Wrapping(0)); |
| assert_eq!(q.num_added, Wrapping(0)); |
| assert!(!q.event_idx_enabled); |
| } |
| |
| #[test] |
| fn test_needs_notification() { |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let qsize = 16; |
| let vq = MockSplitQueue::new(mem, qsize); |
| let mut q: Queue = vq.create_queue().unwrap(); |
| let avail_addr = vq.avail_addr(); |
| |
| // It should always return true when EVENT_IDX isn't enabled. |
| for i in 0..qsize { |
| q.next_used = Wrapping(i); |
| assert!(q.needs_notification(mem).unwrap()); |
| } |
| |
| mem.write_obj::<u16>( |
| u16::to_le(4), |
| avail_addr.unchecked_add(4 + qsize as u64 * 2), |
| ) |
| .unwrap(); |
| q.set_event_idx(true); |
| |
| // Incrementing up to this value causes an `u16` to wrap back to 0. |
| let wrap = u32::from(u16::MAX) + 1; |
| |
| for i in 0..wrap + 12 { |
| q.next_used = Wrapping(i as u16); |
| // Let's test wrapping around the maximum index value as well. |
| // `num_added` needs to be at least `1` to represent the fact that new descriptor |
| // chains have be added to the used ring since the last time `needs_notification` |
| // returned. |
| q.num_added = Wrapping(1); |
| let expected = i == 5 || i == (5 + wrap); |
| assert_eq!((q.needs_notification(mem).unwrap(), i), (expected, i)); |
| } |
| |
| mem.write_obj::<u16>( |
| u16::to_le(8), |
| avail_addr.unchecked_add(4 + qsize as u64 * 2), |
| ) |
| .unwrap(); |
| |
| // Returns `false` because the current `used_event` value is behind both `next_used` and |
| // the value of `next_used` at the time when `needs_notification` last returned (which is |
| // computed based on `num_added` as described in the comments for `needs_notification`. |
| assert!(!q.needs_notification(mem).unwrap()); |
| |
| mem.write_obj::<u16>( |
| u16::to_le(15), |
| avail_addr.unchecked_add(4 + qsize as u64 * 2), |
| ) |
| .unwrap(); |
| |
| q.num_added = Wrapping(1); |
| assert!(!q.needs_notification(mem).unwrap()); |
| |
| q.next_used = Wrapping(15); |
| q.num_added = Wrapping(1); |
| assert!(!q.needs_notification(mem).unwrap()); |
| |
| q.next_used = Wrapping(16); |
| q.num_added = Wrapping(1); |
| assert!(q.needs_notification(mem).unwrap()); |
| |
| // Calling `needs_notification` again immediately returns `false`. |
| assert!(!q.needs_notification(mem).unwrap()); |
| |
| mem.write_obj::<u16>( |
| u16::to_le(u16::MAX - 3), |
| avail_addr.unchecked_add(4 + qsize as u64 * 2), |
| ) |
| .unwrap(); |
| q.next_used = Wrapping(u16::MAX - 2); |
| q.num_added = Wrapping(1); |
| // Returns `true` because, when looking at circular sequence of indices of the used ring, |
| // the value we wrote in the `used_event` appears between the "old" value of `next_used` |
| // (i.e. `next_used` - `num_added`) and the current `next_used`, thus suggesting that we |
| // need to notify the driver. |
| assert!(q.needs_notification(mem).unwrap()); |
| } |
| |
| #[test] |
| fn test_enable_disable_notification() { |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(mem, 16); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| let used_addr = vq.used_addr(); |
| |
| assert!(!q.event_idx_enabled); |
| |
| q.enable_notification(mem).unwrap(); |
| let v = mem.read_obj::<u16>(used_addr).map(u16::from_le).unwrap(); |
| assert_eq!(v, 0); |
| |
| q.disable_notification(mem).unwrap(); |
| let v = mem.read_obj::<u16>(used_addr).map(u16::from_le).unwrap(); |
| assert_eq!(v, VRING_USED_F_NO_NOTIFY as u16); |
| |
| q.enable_notification(mem).unwrap(); |
| let v = mem.read_obj::<u16>(used_addr).map(u16::from_le).unwrap(); |
| assert_eq!(v, 0); |
| |
| q.set_event_idx(true); |
| let avail_addr = vq.avail_addr(); |
| mem.write_obj::<u16>(u16::to_le(2), avail_addr.unchecked_add(2)) |
| .unwrap(); |
| |
| assert!(q.enable_notification(mem).unwrap()); |
| q.next_avail = Wrapping(2); |
| assert!(!q.enable_notification(mem).unwrap()); |
| |
| mem.write_obj::<u16>(u16::to_le(8), avail_addr.unchecked_add(2)) |
| .unwrap(); |
| |
| assert!(q.enable_notification(mem).unwrap()); |
| q.next_avail = Wrapping(8); |
| assert!(!q.enable_notification(mem).unwrap()); |
| } |
| |
| #[test] |
| fn test_consume_chains_with_notif() { |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(mem, 16); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // q is currently valid. |
| assert!(q.is_valid(mem)); |
| |
| // The chains are (0, 1), (2, 3, 4), (5, 6), (7, 8), (9, 10, 11, 12). |
| let mut descs = Vec::new(); |
| for i in 0..13 { |
| let flags = match i { |
| 1 | 4 | 6 | 8 | 12 => 0, |
| _ => VRING_DESC_F_NEXT, |
| }; |
| |
| descs.push(Descriptor::new( |
| (0x1000 * (i + 1)) as u64, |
| 0x1000, |
| flags as u16, |
| i + 1, |
| )); |
| } |
| |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| // Update the index of the chain that can be consumed to not be the last one. |
| // This enables us to consume chains in multiple iterations as opposed to consuming |
| // all the driver written chains at once. |
| vq.avail().idx().store(u16::to_le(2)); |
| // No descriptor chains are consumed at this point. |
| assert_eq!(q.next_avail(), 0); |
| |
| let mut i = 0; |
| |
| loop { |
| i += 1; |
| q.disable_notification(mem).unwrap(); |
| |
| while let Some(chain) = q.iter(mem).unwrap().next() { |
| // Process the descriptor chain, and then add entries to the |
| // used ring. |
| let head_index = chain.head_index(); |
| let mut desc_len = 0; |
| chain.for_each(|d| { |
| if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { |
| desc_len += d.len(); |
| } |
| }); |
| q.add_used(mem, head_index, desc_len).unwrap(); |
| } |
| if !q.enable_notification(mem).unwrap() { |
| break; |
| } |
| } |
| // The chains should be consumed in a single loop iteration because there's nothing updating |
| // the `idx` field of the available ring in the meantime. |
| assert_eq!(i, 1); |
| // The next chain that can be consumed should have index 2. |
| assert_eq!(q.next_avail(), 2); |
| assert_eq!(q.next_used(), 2); |
| // Let the device know it can consume one more chain. |
| vq.avail().idx().store(u16::to_le(3)); |
| i = 0; |
| |
| loop { |
| i += 1; |
| q.disable_notification(mem).unwrap(); |
| |
| while let Some(chain) = q.iter(mem).unwrap().next() { |
| // Process the descriptor chain, and then add entries to the |
| // used ring. |
| let head_index = chain.head_index(); |
| let mut desc_len = 0; |
| chain.for_each(|d| { |
| if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { |
| desc_len += d.len(); |
| } |
| }); |
| q.add_used(mem, head_index, desc_len).unwrap(); |
| } |
| |
| // For the simplicity of the test we are updating here the `idx` value of the available |
| // ring. Ideally this should be done on a separate thread. |
| // Because of this update, the loop should be iterated again to consume the new |
| // available descriptor chains. |
| vq.avail().idx().store(u16::to_le(4)); |
| if !q.enable_notification(mem).unwrap() { |
| break; |
| } |
| } |
| assert_eq!(i, 2); |
| // The next chain that can be consumed should have index 4. |
| assert_eq!(q.next_avail(), 4); |
| assert_eq!(q.next_used(), 4); |
| |
| // Set an `idx` that is bigger than the number of entries added in the ring. |
| // This is an allowed scenario, but the indexes of the chain will have unexpected values. |
| vq.avail().idx().store(u16::to_le(7)); |
| loop { |
| q.disable_notification(mem).unwrap(); |
| |
| while let Some(chain) = q.iter(mem).unwrap().next() { |
| // Process the descriptor chain, and then add entries to the |
| // used ring. |
| let head_index = chain.head_index(); |
| let mut desc_len = 0; |
| chain.for_each(|d| { |
| if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { |
| desc_len += d.len(); |
| } |
| }); |
| q.add_used(mem, head_index, desc_len).unwrap(); |
| } |
| if !q.enable_notification(mem).unwrap() { |
| break; |
| } |
| } |
| assert_eq!(q.next_avail(), 7); |
| assert_eq!(q.next_used(), 7); |
| } |
| |
| #[test] |
| fn test_invalid_avail_idx() { |
| // This is a negative test for the following MUST from the spec: `A driver MUST NOT |
| // decrement the available idx on a virtqueue (ie. there is no way to “unexpose” buffers).`. |
| // We validate that for this misconfiguration, the device does not panic. |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(mem, 16); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // q is currently valid. |
| assert!(q.is_valid(mem)); |
| |
| // The chains are (0, 1), (2, 3, 4), (5, 6). |
| let mut descs = Vec::new(); |
| for i in 0..7 { |
| let flags = match i { |
| 1 | 4 | 6 => 0, |
| _ => VRING_DESC_F_NEXT, |
| }; |
| |
| descs.push(Descriptor::new( |
| (0x1000 * (i + 1)) as u64, |
| 0x1000, |
| flags as u16, |
| i + 1, |
| )); |
| } |
| |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| // Let the device know it can consume chains with the index < 2. |
| vq.avail().idx().store(u16::to_le(3)); |
| // No descriptor chains are consumed at this point. |
| assert_eq!(q.next_avail(), 0); |
| assert_eq!(q.next_used(), 0); |
| |
| loop { |
| q.disable_notification(mem).unwrap(); |
| |
| while let Some(chain) = q.iter(mem).unwrap().next() { |
| // Process the descriptor chain, and then add entries to the |
| // used ring. |
| let head_index = chain.head_index(); |
| let mut desc_len = 0; |
| chain.for_each(|d| { |
| if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { |
| desc_len += d.len(); |
| } |
| }); |
| q.add_used(mem, head_index, desc_len).unwrap(); |
| } |
| if !q.enable_notification(mem).unwrap() { |
| break; |
| } |
| } |
| // The next chain that can be consumed should have index 3. |
| assert_eq!(q.next_avail(), 3); |
| assert_eq!(q.avail_idx(mem, Ordering::Acquire).unwrap(), Wrapping(3)); |
| assert_eq!(q.next_used(), 3); |
| assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(3)); |
| assert!(q.lock().ready()); |
| |
| // Decrement `idx` which should be forbidden. We don't enforce this thing, but we should |
| // test that we don't panic in case the driver decrements it. |
| vq.avail().idx().store(u16::to_le(1)); |
| // Invalid available ring index |
| assert!(q.iter(mem).is_err()); |
| } |
| |
| #[test] |
| fn test_iterator_and_avail_idx() { |
| // This test ensures constructing a descriptor chain iterator succeeds |
| // with valid available ring indexes while produces an error with invalid |
| // indexes. |
| let queue_size = 2; |
| let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(mem, queue_size); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // q is currently valid. |
| assert!(q.is_valid(mem)); |
| |
| // Create descriptors to fill up the queue |
| let mut descs = Vec::new(); |
| for i in 0..queue_size { |
| descs.push(Descriptor::new( |
| (0x1000 * (i + 1)) as u64, |
| 0x1000, |
| 0_u16, |
| i + 1, |
| )); |
| } |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| |
| // Set the 'next_available' index to 'u16:MAX' to test the wrapping scenarios |
| q.set_next_avail(u16::MAX); |
| |
| // When the number of chains exposed by the driver is equal to or less than the queue |
| // size, the available ring index is valid and constructs an iterator successfully. |
| let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size); |
| vq.avail().idx().store(u16::to_le(avail_idx.0)); |
| assert!(q.iter(mem).is_ok()); |
| let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size - 1); |
| vq.avail().idx().store(u16::to_le(avail_idx.0)); |
| assert!(q.iter(mem).is_ok()); |
| |
| // When the number of chains exposed by the driver is larger than the queue size, the |
| // available ring index is invalid and produces an error from constructing an iterator. |
| let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size + 1); |
| vq.avail().idx().store(u16::to_le(avail_idx.0)); |
| assert!(q.iter(mem).is_err()); |
| } |
| |
| #[test] |
| fn test_descriptor_and_iterator() { |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 16); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // q is currently valid |
| assert!(q.is_valid(m)); |
| |
| // the chains are (0, 1), (2, 3, 4) and (5, 6) |
| let mut descs = Vec::new(); |
| for j in 0..7 { |
| let flags = match j { |
| 1 | 6 => 0, |
| 2 | 5 => VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, |
| 4 => VRING_DESC_F_WRITE, |
| _ => VRING_DESC_F_NEXT, |
| }; |
| |
| descs.push(Descriptor::new( |
| (0x1000 * (j + 1)) as u64, |
| 0x1000, |
| flags as u16, |
| j + 1, |
| )); |
| } |
| |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| |
| let mut i = q.iter(m).unwrap(); |
| |
| { |
| let c = i.next().unwrap(); |
| assert_eq!(c.head_index(), 0); |
| |
| let mut iter = c; |
| assert!(iter.next().is_some()); |
| assert!(iter.next().is_some()); |
| assert!(iter.next().is_none()); |
| assert!(iter.next().is_none()); |
| } |
| |
| { |
| let c = i.next().unwrap(); |
| assert_eq!(c.head_index(), 2); |
| |
| let mut iter = c.writable(); |
| assert!(iter.next().is_some()); |
| assert!(iter.next().is_some()); |
| assert!(iter.next().is_none()); |
| assert!(iter.next().is_none()); |
| } |
| |
| { |
| let c = i.next().unwrap(); |
| assert_eq!(c.head_index(), 5); |
| |
| let mut iter = c.readable(); |
| assert!(iter.next().is_some()); |
| assert!(iter.next().is_none()); |
| assert!(iter.next().is_none()); |
| } |
| } |
| |
| #[test] |
| fn test_iterator() { |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 16); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| q.size = q.max_size; |
| q.desc_table = vq.desc_table_addr(); |
| q.avail_ring = vq.avail_addr(); |
| q.used_ring = vq.used_addr(); |
| assert!(q.is_valid(m)); |
| |
| { |
| // an invalid queue should return an iterator with no next |
| q.ready = false; |
| assert!(q.iter(m).is_err()); |
| } |
| |
| q.ready = true; |
| |
| // now let's create two simple descriptor chains |
| // the chains are (0, 1) and (2, 3, 4) |
| { |
| let mut descs = Vec::new(); |
| for j in 0..5u16 { |
| let flags = match j { |
| 1 | 4 => 0, |
| _ => VRING_DESC_F_NEXT, |
| }; |
| |
| descs.push(Descriptor::new( |
| (0x1000 * (j + 1)) as u64, |
| 0x1000, |
| flags as u16, |
| j + 1, |
| )); |
| } |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| |
| let mut i = q.iter(m).unwrap(); |
| |
| { |
| let mut c = i.next().unwrap(); |
| assert_eq!(c.head_index(), 0); |
| |
| c.next().unwrap(); |
| assert!(c.next().is_some()); |
| assert!(c.next().is_none()); |
| assert_eq!(c.head_index(), 0); |
| } |
| |
| { |
| let mut c = i.next().unwrap(); |
| assert_eq!(c.head_index(), 2); |
| |
| c.next().unwrap(); |
| c.next().unwrap(); |
| c.next().unwrap(); |
| assert!(c.next().is_none()); |
| assert_eq!(c.head_index(), 2); |
| } |
| |
| // also test go_to_previous_position() works as expected |
| { |
| assert!(i.next().is_none()); |
| i.go_to_previous_position(); |
| let mut c = q.iter(m).unwrap().next().unwrap(); |
| c.next().unwrap(); |
| c.next().unwrap(); |
| c.next().unwrap(); |
| assert!(c.next().is_none()); |
| } |
| } |
| |
| // Test that iterating some broken descriptor chain does not exceed |
| // 2^32 bytes in total (VIRTIO spec version 1.2, 2.7.5.2: |
| // Drivers MUST NOT add a descriptor chain longer than 2^32 bytes in |
| // total) |
| { |
| let descs = vec![ |
| Descriptor::new(0x1000, 0xffff_ffff, VRING_DESC_F_NEXT as u16, 1), |
| Descriptor::new(0x1000, 0x1234_5678, 0, 2), |
| ]; |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| let mut yielded_bytes_by_iteration = 0_u32; |
| for d in q.iter(m).unwrap().next().unwrap() { |
| yielded_bytes_by_iteration = yielded_bytes_by_iteration |
| .checked_add(d.len()) |
| .expect("iterator should not yield more than 2^32 bytes"); |
| } |
| } |
| |
| // Same as above, but test with a descriptor which is self-referential |
| { |
| let descs = vec![Descriptor::new( |
| 0x1000, |
| 0xffff_ffff, |
| VRING_DESC_F_NEXT as u16, |
| 0, |
| )]; |
| vq.add_desc_chains(&descs, 0).unwrap(); |
| let mut yielded_bytes_by_iteration = 0_u32; |
| for d in q.iter(m).unwrap().next().unwrap() { |
| yielded_bytes_by_iteration = yielded_bytes_by_iteration |
| .checked_add(d.len()) |
| .expect("iterator should not yield more than 2^32 bytes"); |
| } |
| } |
| } |
| |
| #[test] |
| fn test_regression_iterator_division() { |
| // This is a regression test that tests that the iterator does not try to divide |
| // by 0 when the queue size is 0 |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 1); |
| // This input was generated by the fuzzer, both for the QueueS and the Descriptor |
| let descriptors: Vec<Descriptor> = vec![Descriptor::new( |
| 14178673876262995140, |
| 3301229764, |
| 50372, |
| 50372, |
| )]; |
| vq.build_desc_chain(&descriptors).unwrap(); |
| |
| let mut q = Queue { |
| max_size: 38, |
| next_avail: Wrapping(0), |
| next_used: Wrapping(0), |
| event_idx_enabled: false, |
| num_added: Wrapping(0), |
| size: 0, |
| ready: false, |
| desc_table: GuestAddress(12837708984796196), |
| avail_ring: GuestAddress(0), |
| used_ring: GuestAddress(9943947977301164032), |
| }; |
| |
| assert!(q.pop_descriptor_chain(m).is_none()); |
| } |
| |
| #[test] |
| fn test_setters_error_cases() { |
| assert_eq!(Queue::new(15).unwrap_err(), Error::InvalidMaxSize); |
| let mut q = Queue::new(16).unwrap(); |
| |
| let expected_val = q.desc_table.0; |
| assert_eq!( |
| q.try_set_desc_table_address(GuestAddress(0xf)).unwrap_err(), |
| Error::InvalidDescTableAlign |
| ); |
| assert_eq!(q.desc_table(), expected_val); |
| |
| let expected_val = q.avail_ring.0; |
| assert_eq!( |
| q.try_set_avail_ring_address(GuestAddress(0x1)).unwrap_err(), |
| Error::InvalidAvailRingAlign |
| ); |
| assert_eq!(q.avail_ring(), expected_val); |
| |
| let expected_val = q.used_ring.0; |
| assert_eq!( |
| q.try_set_used_ring_address(GuestAddress(0x3)).unwrap_err(), |
| Error::InvalidUsedRingAlign |
| ); |
| assert_eq!(q.used_ring(), expected_val); |
| |
| let expected_val = q.size; |
| assert_eq!(q.try_set_size(15).unwrap_err(), Error::InvalidSize); |
| assert_eq!(q.size(), expected_val) |
| } |
| |
| #[test] |
| // This is a regression test for a fuzzing finding. If the driver requests a reset of the |
| // device, but then does not re-initializes the queue then a subsequent call to process |
| // a request should yield no descriptors to process. Before this fix we were processing |
| // descriptors that were added to the queue before, and we were ending up processing 255 |
| // descriptors per chain. |
| fn test_regression_timeout_after_reset() { |
| // The input below was generated by libfuzzer and adapted for this test. |
| let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x0), 0x10000)]).unwrap(); |
| let vq = MockSplitQueue::new(m, 1024); |
| |
| // This input below was generated by the fuzzer. |
| let descriptors: Vec<Descriptor> = vec![ |
| Descriptor::new(21508325467, 0, 1, 4), |
| Descriptor::new(2097152, 4096, 3, 0), |
| Descriptor::new(18374686479672737792, 4294967295, 65535, 29), |
| Descriptor::new(76842670169653248, 1114115, 0, 0), |
| Descriptor::new(16, 983040, 126, 3), |
| Descriptor::new(897648164864, 0, 0, 0), |
| Descriptor::new(111669149722, 0, 0, 0), |
| ]; |
| vq.build_multiple_desc_chains(&descriptors).unwrap(); |
| |
| let mut q: Queue = vq.create_queue().unwrap(); |
| |
| // Setting the queue to ready should not allow consuming descriptors after reset. |
| q.reset(); |
| q.set_ready(true); |
| let mut counter = 0; |
| while let Some(mut desc_chain) = q.pop_descriptor_chain(m) { |
| // this empty loop is here to check that there are no side effects |
| // in terms of memory & execution time. |
| while desc_chain.next().is_some() { |
| counter += 1; |
| } |
| } |
| assert_eq!(counter, 0); |
| |
| // Setting the avail_addr to valid should not allow consuming descriptors after reset. |
| q.reset(); |
| q.set_avail_ring_address(Some(0x1000), None); |
| assert_eq!(q.avail_ring, GuestAddress(0x1000)); |
| counter = 0; |
| while let Some(mut desc_chain) = q.pop_descriptor_chain(m) { |
| // this empty loop is here to check that there are no side effects |
| // in terms of memory & execution time. |
| while desc_chain.next().is_some() { |
| counter += 1; |
| } |
| } |
| assert_eq!(counter, 0); |
| } |
| } |