Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions lib/propolis/src/hw/virtio/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,7 @@ pub struct Info {

pub struct VirtQueues {
len: AtomicUsize,
peak: AtomicUsize,
queues: Vec<Arc<VirtQueue>>,
}

Expand All @@ -947,25 +948,45 @@ impl VirtQueues {
Self::new_with_len(sizes.len(), sizes)
}

pub fn new_with_len(len: usize, sizes: &[VqSize]) -> Self {
pub fn new_with_len(initial_len: usize, sizes: &[VqSize]) -> Self {
assert!(
0 < len && len <= sizes.len() && sizes.len() <= MAX_QUEUES,
0 < initial_len
&& initial_len <= sizes.len()
&& sizes.len() <= MAX_QUEUES,
"virtqueue size must be positive u16 and len must be smaller pos"
);
let queues = sizes
.into_iter()
.enumerate()
.map(|(id, size)| Arc::new(VirtQueue::new(id as u16, *size)))
.collect::<Vec<_>>();
let len = AtomicUsize::new(len);
Self { len, queues }
let len = AtomicUsize::new(initial_len);
let peak = AtomicUsize::new(initial_len);
Self { len, peak, queues }
}

pub fn set_len(&self, len: usize) -> Result<(), usize> {
if len == 0 || len > self.max_capacity() {
return Err(len);
}
self.len.store(len, Ordering::Release);
let mut peak = self.peak.load(Ordering::Acquire);
while len > peak {
match self.peak.compare_exchange(
peak,
len,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => {
// We've updated the peak, all done
break;
}
Err(next_peak) => {
peak = next_peak;
}
}
}
Ok(())
}

Expand All @@ -978,6 +999,10 @@ impl VirtQueues {
self.len.load(Ordering::Relaxed)
}

pub fn peak(&self) -> usize {
self.peak.load(Ordering::Relaxed)
}

pub const fn max_capacity(&self) -> usize {
self.queues.len()
}
Expand Down Expand Up @@ -1005,13 +1030,13 @@ impl VirtQueues {
self.queues[..len].iter().chain([self.get_control()])
}

/// Iterate all queues, regardless of the device's current configuration
/// happening to use them or not.
///
/// This is primarily useful for operations like device reset and teardown
/// where we need to manage all *possible* device state.
/// Iterate all queues the device may have used; the current number of
/// VirtQueues may be lower than a previous high watermark, but in cases
/// like device reset and teardown we must manage all viona rings
/// corresponding to ever-active VirtQueues.
pub fn iter_all(&self) -> impl std::iter::Iterator<Item = &Arc<VirtQueue>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about only resetting those queues that are not in the initial/reset state themselves? That is, can we examine the state before issuing the reset?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my read is that that truly is allowed-but-pointless, versus the current behavior of resetting queues beyond USE_PAIRS that gets us the EINVAL and is really not

self.queues.iter()
let peak = self.peak() - 1;
self.queues[..peak].iter().chain([self.get_control()])
}

pub fn export(&self) -> migrate::VirtQueuesV1 {
Expand Down
Loading