virtio-devices: Handle virtio queues interrupts from transport layer

Instead of relying on the virtio-queue crate to store the information
about the MSI-X vectors for each queue, we handle this directly from the
PCI transport layer.

This is the first step in getting closer to the upstream version of
virtio-queue so that we can eventually move fully to the upstream
version.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-01-24 15:30:42 +01:00
parent d3081ff50c
commit de3e003e3e
17 changed files with 73 additions and 107 deletions

View File

@ -139,8 +139,7 @@ pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(
&self,
_int_type: &VirtioInterruptType,
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
_int_type: VirtioInterruptType,
) -> std::result::Result<(), std::io::Error> {
Ok(())
}

View File

@ -165,12 +165,8 @@ struct BalloonEpollHandler {
}
impl BalloonEpollHandler {
fn signal(
&self,
int_type: &VirtioInterruptType,
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> result::Result<(), Error> {
self.interrupt_cb.trigger(int_type, queue).map_err(|e| {
fn signal(&self, int_type: VirtioInterruptType) -> result::Result<(), Error> {
self.interrupt_cb.trigger(int_type).map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
Error::FailedSignal(e)
})
@ -268,7 +264,7 @@ impl BalloonEpollHandler {
.map_err(Error::QueueAddUsed)?;
}
if used_count > 0 {
self.signal(&VirtioInterruptType::Queue, Some(&self.queues[queue_index]))?;
self.signal(VirtioInterruptType::Queue(queue_index as u16))?;
}
Ok(())
@ -303,7 +299,7 @@ impl EpollHelperHandler for BalloonEpollHandler {
let mut config = self.config.lock().unwrap();
config.num_pages =
(self.resize_receiver.get_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32;
if let Err(e) = self.signal(&VirtioInterruptType::Config, None) {
if let Err(e) = self.signal(VirtioInterruptType::Config) {
signal_error = true;
Err(e)
} else {

View File

@ -82,6 +82,7 @@ pub struct BlockCounters {
}
struct BlockEpollHandler {
queue_index: u16,
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
mem: GuestMemoryAtomic<GuestMemoryMmap>,
disk_image: Box<dyn AsyncIo>,
@ -258,7 +259,7 @@ impl BlockEpollHandler {
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(&self.queue))
.trigger(VirtioInterruptType::Queue(self.queue_index))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
@ -605,6 +606,7 @@ impl VirtioDevice for Block {
.map_err(ActivateError::CreateRateLimiter)?;
let mut handler = BlockEpollHandler {
queue_index: i as u16,
queue,
mem: mem.clone(),
disk_image: self

View File

@ -199,9 +199,9 @@ impl ConsoleEpollHandler {
used_count > 0
}
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(&self.queues[0]))
.trigger(VirtioInterruptType::Queue(queue_index))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
@ -239,7 +239,7 @@ impl EpollHelperHandler for ConsoleEpollHandler {
error!("Failed to get queue event: {:?}", e);
return true;
} else if self.process_input_queue() {
if let Err(e) = self.signal_used_queue() {
if let Err(e) = self.signal_used_queue(0) {
error!("Failed to signal used queue: {:?}", e);
return true;
}
@ -249,8 +249,11 @@ impl EpollHelperHandler for ConsoleEpollHandler {
if let Err(e) = self.output_queue_evt.read() {
error!("Failed to get queue event: {:?}", e);
return true;
} else {
self.process_output_queue();
} else if self.process_output_queue() {
if let Err(e) = self.signal_used_queue(1) {
error!("Failed to signal used queue: {:?}", e);
return true;
}
}
}
INPUT_EVENT => {
@ -258,7 +261,7 @@ impl EpollHelperHandler for ConsoleEpollHandler {
error!("Failed to get input event: {:?}", e);
return true;
} else if self.process_input_queue() {
if let Err(e) = self.signal_used_queue() {
if let Err(e) = self.signal_used_queue(0) {
error!("Failed to signal used queue: {:?}", e);
return true;
}
@ -268,10 +271,7 @@ impl EpollHelperHandler for ConsoleEpollHandler {
if let Err(e) = self.config_evt.read() {
error!("Failed to get config event: {:?}", e);
return true;
} else if let Err(e) = self
.interrupt_cb
.trigger(&VirtioInterruptType::Config, None)
{
} else if let Err(e) = self.interrupt_cb.trigger(VirtioInterruptType::Config) {
error!("Failed to signal console driver: {:?}", e);
return true;
}
@ -293,7 +293,7 @@ impl EpollHelperHandler for ConsoleEpollHandler {
}
if self.process_input_queue() {
if let Err(e) = self.signal_used_queue() {
if let Err(e) = self.signal_used_queue(0) {
error!("Failed to signal used queue: {:?}", e);
return true;
}
@ -492,7 +492,7 @@ impl VirtioDevice for Console {
.store(self.common.acked_features, Ordering::Relaxed);
if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
if let Err(e) = interrupt_cb.trigger(&VirtioInterruptType::Config, None) {
if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
error!("Failed to signal console driver: {:?}", e);
}
}

View File

@ -25,20 +25,12 @@ use vmm_sys_util::eventfd::EventFd;
pub enum VirtioInterruptType {
Config,
Queue,
Queue(u16),
}
pub trait VirtioInterrupt: Send + Sync {
fn trigger(
&self,
int_type: &VirtioInterruptType,
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> std::result::Result<(), std::io::Error>;
fn notifier(
&self,
_int_type: &VirtioInterruptType,
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> Option<EventFd> {
fn trigger(&self, int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error>;
fn notifier(&self, _int_type: VirtioInterruptType) -> Option<EventFd> {
None
}
}

View File

@ -609,12 +609,9 @@ impl IommuEpollHandler {
false
}
fn signal_used_queue(
&self,
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
) -> result::Result<(), DeviceError> {
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(queue))
.trigger(VirtioInterruptType::Queue(queue_index))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
@ -644,7 +641,7 @@ impl EpollHelperHandler for IommuEpollHandler {
error!("Failed to get queue event: {:?}", e);
return true;
} else if self.request_queue() {
if let Err(e) = self.signal_used_queue(&self.queues[0]) {
if let Err(e) = self.signal_used_queue(0) {
error!("Failed to signal used queue: {:?}", e);
return true;
}
@ -655,7 +652,7 @@ impl EpollHelperHandler for IommuEpollHandler {
error!("Failed to get queue event: {:?}", e);
return true;
} else if self.event_queue() {
if let Err(e) = self.signal_used_queue(&self.queues[1]) {
if let Err(e) = self.signal_used_queue(1) {
error!("Failed to signal used queue: {:?}", e);
return true;
}

View File

@ -644,13 +644,11 @@ impl MemEpollHandler {
(resp_type, resp_state)
}
fn signal(&self, int_type: &VirtioInterruptType) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(int_type, Some(&self.queue))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
})
fn signal(&self, int_type: VirtioInterruptType) -> result::Result<(), DeviceError> {
self.interrupt_cb.trigger(int_type).map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
})
}
fn process_queue(&mut self) -> bool {
@ -734,7 +732,7 @@ impl EpollHelperHandler for MemEpollHandler {
let mut r = config.resize(size);
r = match r {
Err(e) => Err(e),
_ => match self.signal(&VirtioInterruptType::Config) {
_ => match self.signal(VirtioInterruptType::Config) {
Err(e) => {
signal_error = true;
Err(Error::ResizeTriggerFail(e))
@ -756,7 +754,7 @@ impl EpollHelperHandler for MemEpollHandler {
error!("Failed to get queue event: {:?}", e);
return true;
} else if self.process_queue() {
if let Err(e) = self.signal(&VirtioInterruptType::Queue) {
if let Err(e) = self.signal(VirtioInterruptType::Queue(0)) {
error!("Failed to signal used queue: {:?}", e);
return true;
}

View File

@ -124,6 +124,7 @@ struct NetEpollHandler {
interrupt_cb: Arc<dyn VirtioInterrupt>,
kill_evt: EventFd,
pause_evt: EventFd,
queue_index_base: u16,
queue_pair: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
queue_evt_pair: Vec<EventFd>,
// Always generate interrupts until the driver has signalled to the device.
@ -134,12 +135,9 @@ struct NetEpollHandler {
}
impl NetEpollHandler {
fn signal_used_queue(
&self,
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
) -> result::Result<(), DeviceError> {
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(queue))
.trigger(VirtioInterruptType::Queue(queue_index))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
@ -182,7 +180,7 @@ impl NetEpollHandler {
.map_err(DeviceError::NetQueuePair)?
|| !self.driver_awake
{
self.signal_used_queue(&self.queue_pair[1])?;
self.signal_used_queue(self.queue_index_base + 1)?;
debug!("Signalling TX queue");
} else {
debug!("Not signalling TX queue");
@ -211,7 +209,7 @@ impl NetEpollHandler {
.map_err(DeviceError::NetQueuePair)?
|| !self.driver_awake
{
self.signal_used_queue(&self.queue_pair[0])?;
self.signal_used_queue(self.queue_index_base)?;
debug!("Signalling RX queue");
} else {
debug!("Not signalling RX queue");
@ -651,6 +649,7 @@ impl VirtioDevice for Net {
rx_rate_limiter,
tx_rate_limiter,
},
queue_index_base: (i * 2) as u16,
queue_pair,
queue_evt_pair,
interrupt_cb: interrupt_cb.clone(),

View File

@ -213,7 +213,7 @@ impl PmemEpollHandler {
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(&self.queue))
.trigger(VirtioInterruptType::Queue(0))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)

View File

@ -76,7 +76,7 @@ impl RngEpollHandler {
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(&self.queues[0]))
.trigger(VirtioInterruptType::Queue(0))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)

View File

@ -24,6 +24,7 @@ pub struct VirtioPciCommonConfigState {
pub driver_feature_select: u32,
pub queue_select: u16,
pub msix_config: u16,
pub msix_queues: Vec<u16>,
}
impl VersionMapped for VirtioPciCommonConfigState {}
@ -57,6 +58,7 @@ pub struct VirtioPciCommonConfig {
pub driver_feature_select: u32,
pub queue_select: u16,
pub msix_config: Arc<AtomicU16>,
pub msix_queues: Arc<Mutex<Vec<u16>>>,
}
impl VirtioPciCommonConfig {
@ -68,6 +70,7 @@ impl VirtioPciCommonConfig {
driver_feature_select: self.driver_feature_select,
queue_select: self.queue_select,
msix_config: self.msix_config.load(Ordering::Acquire),
msix_queues: self.msix_queues.lock().unwrap().clone(),
}
}
@ -78,6 +81,7 @@ impl VirtioPciCommonConfig {
self.driver_feature_select = state.driver_feature_select;
self.queue_select = state.queue_select;
self.msix_config.store(state.msix_config, Ordering::Release);
*(self.msix_queues.lock().unwrap()) = state.msix_queues.clone();
}
pub fn read(
@ -164,7 +168,7 @@ impl VirtioPciCommonConfig {
0x12 => queues.len() as u16, // num_queues
0x16 => self.queue_select,
0x18 => self.with_queue(queues, |q| q.state.size).unwrap_or(0),
0x1a => self.with_queue(queues, |q| q.state.vector).unwrap_or(0),
0x1a => self.msix_queues.lock().unwrap()[self.queue_select as usize],
0x1c => {
if self.with_queue(queues, |q| q.state.ready).unwrap_or(false) {
1
@ -191,7 +195,7 @@ impl VirtioPciCommonConfig {
0x10 => self.msix_config.store(value, Ordering::Release),
0x16 => self.queue_select = value,
0x18 => self.with_queue_mut(queues, |q| q.state.size = value),
0x1a => self.with_queue_mut(queues, |q| q.state.vector = value),
0x1a => self.msix_queues.lock().unwrap()[self.queue_select as usize] = value,
0x1c => self.with_queue_mut(queues, |q| q.enable(value == 1)),
_ => {
warn!("invalid virtio register word write: 0x{:x}", offset);
@ -376,6 +380,7 @@ mod tests {
driver_feature_select: 0x0,
queue_select: 0xff,
msix_config: Arc::new(AtomicU16::new(0)),
msix_queues: Arc::new(Mutex::new(vec![0; 3])),
};
let dev = Arc::new(Mutex::new(DummyDevice(0)));

View File

@ -267,7 +267,6 @@ struct QueueState {
max_size: u16,
size: u16,
ready: bool,
vector: u16,
desc_table: u64,
avail_ring: u64,
used_ring: u64,
@ -358,6 +357,7 @@ impl VirtioPciDevice {
for _ in locked_device.queue_max_sizes().iter() {
queue_evts.push(EventFd::new(EFD_NONBLOCK)?)
}
let num_queues = locked_device.queue_max_sizes().len();
let queues = locked_device
.queue_max_sizes()
.iter()
@ -429,6 +429,7 @@ impl VirtioPciDevice {
driver_feature_select: 0,
queue_select: 0,
msix_config: Arc::new(AtomicU16::new(VIRTQ_MSI_NO_VECTOR)),
msix_queues: Arc::new(Mutex::new(vec![VIRTQ_MSI_NO_VECTOR; num_queues])),
},
msix_config,
msix_num,
@ -453,6 +454,7 @@ impl VirtioPciDevice {
virtio_pci_device.virtio_interrupt = Some(Arc::new(VirtioInterruptMsix::new(
msix_config.clone(),
virtio_pci_device.common_config.msix_config.clone(),
virtio_pci_device.common_config.msix_queues.clone(),
virtio_pci_device.interrupt_source_group.clone(),
)));
}
@ -471,7 +473,6 @@ impl VirtioPciDevice {
max_size: q.max_size(),
size: q.state.size,
ready: q.state.ready,
vector: q.state.vector,
desc_table: q.state.desc_table.0,
avail_ring: q.state.avail_ring.0,
used_ring: q.state.used_ring.0,
@ -490,7 +491,6 @@ impl VirtioPciDevice {
for (i, queue) in self.queues.iter_mut().enumerate() {
queue.state.size = state.queues[i].size;
queue.state.ready = state.queues[i].ready;
queue.state.vector = state.queues[i].vector;
queue.state.desc_table = GuestAddress(state.queues[i].desc_table);
queue.state.avail_ring = GuestAddress(state.queues[i].avail_ring);
queue.state.used_ring = GuestAddress(state.queues[i].used_ring);
@ -716,6 +716,7 @@ impl VirtioTransport for VirtioPciDevice {
pub struct VirtioInterruptMsix {
msix_config: Arc<Mutex<MsixConfig>>,
config_vector: Arc<AtomicU16>,
queues_vectors: Arc<Mutex<Vec<u16>>>,
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
}
@ -723,30 +724,24 @@ impl VirtioInterruptMsix {
pub fn new(
msix_config: Arc<Mutex<MsixConfig>>,
config_vector: Arc<AtomicU16>,
queues_vectors: Arc<Mutex<Vec<u16>>>,
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
) -> Self {
VirtioInterruptMsix {
msix_config,
config_vector,
queues_vectors,
interrupt_source_group,
}
}
}
impl VirtioInterrupt for VirtioInterruptMsix {
fn trigger(
&self,
int_type: &VirtioInterruptType,
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> std::result::Result<(), std::io::Error> {
fn trigger(&self, int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
let vector = match int_type {
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
VirtioInterruptType::Queue => {
if let Some(q) = queue {
q.state.vector
} else {
0
}
VirtioInterruptType::Queue(queue_index) => {
self.queues_vectors.lock().unwrap()[queue_index as usize]
}
};
@ -770,19 +765,11 @@ impl VirtioInterrupt for VirtioInterruptMsix {
.trigger(vector as InterruptIndex)
}
fn notifier(
&self,
int_type: &VirtioInterruptType,
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> Option<EventFd> {
fn notifier(&self, int_type: VirtioInterruptType) -> Option<EventFd> {
let vector = match int_type {
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
VirtioInterruptType::Queue => {
if let Some(q) = queue {
q.state.vector
} else {
0
}
VirtioInterruptType::Queue(queue_index) => {
self.queues_vectors.lock().unwrap()[queue_index as usize]
}
};

View File

@ -253,7 +253,7 @@ impl VhostUserHandle {
.map_err(Error::VhostUserSetVringBase)?;
if let Some(eventfd) =
virtio_interrupt.notifier(&VirtioInterruptType::Queue, Some(&queue))
virtio_interrupt.notifier(VirtioInterruptType::Queue(queue_index as u16))
{
self.vu
.set_vring_call(queue_index, &eventfd)

View File

@ -102,14 +102,11 @@ where
/// Signal the guest driver that we've used some virtio buffers that it had previously made
/// available.
///
fn signal_used_queue(
&self,
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
) -> result::Result<(), DeviceError> {
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
debug!("vsock: raising IRQ");
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(queue))
.trigger(VirtioInterruptType::Queue(queue_index))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)
@ -155,7 +152,7 @@ where
}
if used_count > 0 {
self.signal_used_queue(&self.queues[0])
self.signal_used_queue(0)
} else {
Ok(())
}
@ -198,7 +195,7 @@ where
}
if used_count > 0 {
self.signal_used_queue(&self.queues[1])
self.signal_used_queue(1)
} else {
Ok(())
}
@ -617,8 +614,8 @@ mod tests {
let ctx = test_ctx.create_epoll_handler_context();
let memory = GuestMemoryAtomic::new(test_ctx.mem.clone());
let queue = Queue::new(memory, 256);
assert!(ctx.handler.signal_used_queue(&queue).is_ok());
let _queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>> = Queue::new(memory, 256);
assert!(ctx.handler.signal_used_queue(0).is_ok());
}
}

View File

@ -170,7 +170,7 @@ mod tests {
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use virtio_queue::{defs::VIRTQ_DESC_F_NEXT, defs::VIRTQ_DESC_F_WRITE, Queue};
use virtio_queue::{defs::VIRTQ_DESC_F_NEXT, defs::VIRTQ_DESC_F_WRITE};
use vm_memory::{GuestAddress, GuestMemoryAtomic};
use vm_virtio::queue::testing::VirtQueue as GuestQ;
use vmm_sys_util::eventfd::EventFd;
@ -180,8 +180,7 @@ mod tests {
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(
&self,
_int_type: &VirtioInterruptType,
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
_int_type: VirtioInterruptType,
) -> std::result::Result<(), std::io::Error> {
Ok(())
}

View File

@ -96,7 +96,7 @@ impl WatchdogEpollHandler {
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
self.interrupt_cb
.trigger(&VirtioInterruptType::Queue, Some(&self.queues[0]))
.trigger(VirtioInterruptType::Queue(0))
.map_err(|e| {
error!("Failed to signal used queue: {:?}", e);
DeviceError::FailedSignalingUsedQueue(e)

View File

@ -17,8 +17,8 @@ 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_MSI_NO_VECTOR, VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_F_NO_NOTIFY,
VIRTQ_USED_RING_HEADER_SIZE, VIRTQ_USED_RING_META_SIZE,
VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_F_NO_NOTIFY, VIRTQ_USED_RING_HEADER_SIZE,
VIRTQ_USED_RING_META_SIZE,
};
use crate::{
error, AccessPlatform, AvailIter, Descriptor, Error, QueueStateGuard, QueueStateT,
@ -58,9 +58,6 @@ pub struct QueueState {
/// Guest physical address of the used ring.
pub used_ring: GuestAddress,
/// Interrupt vector
pub vector: u16,
/// Access platform handler
pub access_platform: Option<Arc<dyn AccessPlatform>>,
}
@ -200,7 +197,6 @@ impl QueueStateT for QueueState {
next_used: Wrapping(0),
event_idx_enabled: false,
signalled_used: None,
vector: VIRTQ_MSI_NO_VECTOR,
access_platform: None,
}
}
@ -266,7 +262,6 @@ impl QueueStateT for QueueState {
self.next_used = Wrapping(0);
self.signalled_used = None;
self.event_idx_enabled = false;
self.vector = VIRTQ_MSI_NO_VECTOR;
}
fn lock(&mut self) -> <Self as QueueStateGuard>::G {