virtio-devices: Optimize add_used() usage

Now that we rely on pop_descriptor_chain() rather than iter() to iterate
over a queue, there's no more borrow on the queue itself, meaning we can
invoke add_used() directly for the iteration loop. This simplifies the
processing of the queues for each virtio device, and bring some possible
performance improvement given we don't have to iterate twice over the
list of descriptors to invoke add_used().

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-07-25 14:26:17 +02:00 committed by Rob Bradford
parent 87f57f7c1e
commit a4859ffe85
11 changed files with 111 additions and 179 deletions

View File

@ -62,7 +62,6 @@ impl CtrlQueue {
queue: &mut Queue,
access_platform: Option<&Arc<dyn AccessPlatform>>,
) -> Result<()> {
let mut used_desc_heads = Vec::new();
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
@ -139,7 +138,10 @@ impl CtrlQueue {
)
.map_err(Error::GuestMemory)?;
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
used_desc_heads.push((desc_chain.head_index(), len));
queue
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.map_err(Error::QueueAddUsed)?;
if !queue
.enable_notification(mem)
@ -149,12 +151,6 @@ impl CtrlQueue {
}
}
for (desc_index, len) in used_desc_heads.iter() {
queue
.add_used(mem, *desc_index, *len)
.map_err(Error::QueueAddUsed)?;
}
Ok(())
}
}

View File

@ -120,7 +120,7 @@ impl TxVirtio {
}
queue
.add_used(mem, desc_chain.head_index(), len)
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.map_err(NetQueuePairError::QueueAddUsed)?;
if !queue
@ -262,7 +262,7 @@ impl RxVirtio {
}
queue
.add_used(mem, desc_chain.head_index(), len)
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.map_err(NetQueuePairError::QueueAddUsed)?;
if !queue

View File

@ -124,7 +124,7 @@ impl VhostUserBlkThread {
&mut self,
vring: &mut RwLockWriteGuard<VringState<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> bool {
let mut used_desc_heads = Vec::new();
let mut used_descs = false;
while let Some(mut desc_chain) = vring
.get_queue_mut()
@ -162,37 +162,35 @@ impl VhostUserBlkThread {
}
}
used_desc_heads.push((desc_chain.head_index(), len));
vring
.get_queue_mut()
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
}
let mem = self.mem.memory();
let mut needs_signalling = false;
for (desc_head, len) in used_desc_heads.iter() {
if self.event_idx {
let queue = vring.get_queue_mut();
if queue.add_used(mem.deref(), *desc_head, *len).is_ok() {
if queue.needs_notification(mem.deref()).unwrap() {
debug!("signalling queue");
needs_signalling = true;
} else {
debug!("omitting signal (event_idx)");
}
}
} else {
if self.event_idx {
if vring
.get_queue_mut()
.needs_notification(self.mem.memory().deref())
.unwrap()
{
debug!("signalling queue");
vring
.get_queue_mut()
.add_used(mem.deref(), *desc_head, *len)
.unwrap();
needs_signalling = true;
} else {
debug!("omitting signal (event_idx)");
}
} else {
debug!("signalling queue");
needs_signalling = true;
}
if needs_signalling {
vring.signal_used_queue().unwrap();
}
!used_desc_heads.is_empty()
used_descs
}
}

View File

@ -22,7 +22,6 @@ use libc::EFD_NONBLOCK;
use seccompiler::SeccompAction;
use std::io;
use std::mem::size_of;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::{
@ -31,7 +30,7 @@ use std::sync::{
};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use virtio_queue::{Queue, QueueOwnedT, QueueT};
use virtio_queue::{Queue, QueueT};
use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestMemoryError, GuestMemoryRegion,
@ -227,34 +226,13 @@ impl BalloonEpollHandler {
Self::advise_memory_range(memory, range_base, range_len, libc::MADV_DONTNEED)
}
fn notify_queue(
&mut self,
queue_index: usize,
used_descs: Vec<(u16, u32)>,
) -> result::Result<(), Error> {
let mem = self.mem.memory();
for (desc_index, len) in used_descs.iter() {
self.queues[queue_index]
.add_used(mem.deref(), *desc_index, *len)
.map_err(Error::QueueAddUsed)?;
}
if !used_descs.is_empty() {
self.signal(VirtioInterruptType::Queue(queue_index as u16))?;
}
Ok(())
}
fn process_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
let mut used_descs = Vec::new();
let mut used_descs = false;
while let Some(mut desc_chain) =
self.queues[queue_index].pop_descriptor_chain(self.mem.memory())
{
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
used_descs.push((desc_chain.head_index(), desc.len()));
let data_chunk_size = size_of::<u32>();
// The head contains the request type which MUST be readable.
@ -294,18 +272,24 @@ impl BalloonEpollHandler {
_ => return Err(Error::InvalidQueueIndex(queue_index)),
}
}
self.queues[queue_index]
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
.map_err(Error::QueueAddUsed)?;
used_descs = true;
}
self.notify_queue(queue_index, used_descs)
if used_descs {
self.signal(VirtioInterruptType::Queue(queue_index as u16))
} else {
Ok(())
}
}
fn process_reporting_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
let mem = self.mem.memory();
let mut used_descs = Vec::new();
for mut desc_chain in self.queues[queue_index]
.iter(mem)
.map_err(Error::QueueIterator)?
let mut used_descs = false;
while let Some(mut desc_chain) =
self.queues[queue_index].pop_descriptor_chain(self.mem.memory())
{
let mut descs_len = 0;
while let Some(desc) = desc_chain.next() {
@ -313,10 +297,17 @@ impl BalloonEpollHandler {
Self::release_memory_range(desc_chain.memory(), desc.addr(), desc.len() as usize)?;
}
used_descs.push((desc_chain.head_index(), descs_len));
self.queues[queue_index]
.add_used(desc_chain.memory(), desc_chain.head_index(), descs_len)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
}
self.notify_queue(queue_index, used_descs)
if used_descs {
self.signal(VirtioInterruptType::Queue(queue_index as u16))
} else {
Ok(())
}
}
fn run(

View File

@ -105,8 +105,7 @@ impl BlockEpollHandler {
fn process_queue_submit(&mut self) -> Result<bool> {
let queue = &mut self.queue;
let mut used_desc_heads = Vec::new();
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
@ -166,26 +165,20 @@ impl BlockEpollHandler {
// If no asynchronous operation has been submitted, we can
// simply return the used descriptor.
used_desc_heads.push((desc_chain.head_index(), 0));
used_count += 1;
queue
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
}
}
let mem = self.mem.memory();
for &(desc_index, len) in used_desc_heads.iter() {
queue
.add_used(mem.deref(), desc_index, len)
.map_err(Error::QueueAddUsed)?;
}
Ok(used_count > 0)
Ok(used_descs)
}
fn process_queue_complete(&mut self) -> Result<bool> {
let queue = &mut self.queue;
let mut used_desc_heads = Vec::new();
let mut used_count = 0;
let mut used_descs = false;
let mem = self.mem.memory();
let mut read_bytes = Wrapping(0);
let mut write_bytes = Wrapping(0);
@ -234,14 +227,10 @@ impl BlockEpollHandler {
// checked that the status_addr was valid.
mem.write_obj(status, request.status_addr).unwrap();
used_desc_heads.push((desc_index as u16, len));
used_count += 1;
}
for &(desc_index, len) in used_desc_heads.iter() {
queue
.add_used(mem.deref(), desc_index, len)
.add_used(mem.deref(), desc_index as u16, len)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
}
self.counters
@ -258,7 +247,7 @@ impl BlockEpollHandler {
.read_ops
.fetch_add(read_ops.0, Ordering::AcqRel);
Ok(used_count > 0)
Ok(used_descs)
}
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {

View File

@ -18,7 +18,6 @@ use std::collections::VecDeque;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
@ -137,8 +136,7 @@ impl ConsoleEpollHandler {
fn process_input_queue(&mut self) -> bool {
let mut in_buffer = self.in_buffer.lock().unwrap();
let recv_queue = &mut self.queues[0]; //receiveq
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
if in_buffer.is_empty() {
return false;
@ -159,20 +157,17 @@ impl ConsoleEpollHandler {
break;
}
used_desc_heads[used_count] = (desc_chain.head_index(), len);
used_count += 1;
recv_queue
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
if in_buffer.is_empty() {
break;
}
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
recv_queue.add_used(mem.deref(), desc_index, len).unwrap();
}
used_count > 0
used_descs
}
/*
@ -184,8 +179,7 @@ impl ConsoleEpollHandler {
*/
fn process_output_queue(&mut self) -> bool {
let trans_queue = &mut self.queues[1]; //transmitq
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
let desc = desc_chain.next().unwrap();
@ -198,15 +192,13 @@ impl ConsoleEpollHandler {
);
let _ = out.flush();
}
used_desc_heads[used_count] = (desc_chain.head_index(), desc.len());
used_count += 1;
trans_queue
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
.unwrap();
used_descs = true;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
trans_queue.add_used(mem.deref(), desc_index, len).unwrap();
}
used_count > 0
used_descs
}
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {

View File

@ -17,7 +17,6 @@ use std::fmt::{self, Display};
use std::io;
use std::mem::size_of;
use std::ops::Bound::Included;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
@ -674,8 +673,7 @@ struct IommuEpollHandler {
impl IommuEpollHandler {
fn request_queue(&mut self) -> bool {
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = self.queues[0].pop_descriptor_chain(self.mem.memory()) {
let len = match Request::parse(
&mut desc_chain,
@ -690,17 +688,13 @@ impl IommuEpollHandler {
}
};
used_desc_heads[used_count] = (desc_chain.head_index(), len);
used_count += 1;
self.queues[0]
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
self.queues[0]
.add_used(mem.deref(), desc_index, len)
.unwrap();
}
used_count > 0
used_descs
}
fn event_queue(&mut self) -> bool {

View File

@ -27,7 +27,6 @@ use seccompiler::SeccompAction;
use std::collections::BTreeMap;
use std::io;
use std::mem::size_of;
use std::ops::Deref;
use std::os::unix::io::{AsRawFd, RawFd};
use std::result;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
@ -665,20 +664,10 @@ impl MemEpollHandler {
}
fn process_queue(&mut self) -> bool {
let mut request_list = Vec::new();
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = self.queue.pop_descriptor_chain(self.mem.memory()) {
request_list.push((
desc_chain.head_index(),
Request::parse(&mut desc_chain),
desc_chain.memory().clone(),
));
}
let mem = self.mem.memory();
for (head_index, request, memory) in request_list {
let len = match request {
let len = match Request::parse(&mut desc_chain) {
Err(e) => {
error!("failed parse VirtioMemReq: {:?}", e);
0
@ -687,21 +676,21 @@ impl MemEpollHandler {
VIRTIO_MEM_REQ_PLUG => {
let resp_type =
self.state_change_request(r.req.addr, r.req.nb_blocks, true);
r.send_response(&memory, resp_type, 0u16)
r.send_response(desc_chain.memory(), resp_type, 0u16)
}
VIRTIO_MEM_REQ_UNPLUG => {
let resp_type =
self.state_change_request(r.req.addr, r.req.nb_blocks, false);
r.send_response(&memory, resp_type, 0u16)
r.send_response(desc_chain.memory(), resp_type, 0u16)
}
VIRTIO_MEM_REQ_UNPLUG_ALL => {
let resp_type = self.unplug_all();
r.send_response(&memory, resp_type, 0u16)
r.send_response(desc_chain.memory(), resp_type, 0u16)
}
VIRTIO_MEM_REQ_STATE => {
let (resp_type, resp_state) =
self.state_request(r.req.addr, r.req.nb_blocks);
r.send_response(&memory, resp_type, resp_state)
r.send_response(desc_chain.memory(), resp_type, resp_state)
}
_ => {
error!("VirtioMemReq unknown request type {:?}", r.req.req_type);
@ -710,11 +699,13 @@ impl MemEpollHandler {
},
};
self.queue.add_used(mem.deref(), head_index, len).unwrap();
used_count += 1;
self.queue
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
}
used_count > 0
used_descs
}
fn run(

View File

@ -21,7 +21,6 @@ use std::fmt::{self, Display};
use std::fs::File;
use std::io;
use std::mem::size_of;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::AtomicBool;
@ -178,8 +177,7 @@ struct PmemEpollHandler {
impl PmemEpollHandler {
fn process_queue(&mut self) -> bool {
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = self.queue.pop_descriptor_chain(self.mem.memory()) {
let len = match Request::parse(&mut desc_chain, self.access_platform.as_ref()) {
Ok(ref req) if (req.type_ == RequestType::Flush) => {
@ -211,15 +209,13 @@ impl PmemEpollHandler {
}
};
used_desc_heads[used_count] = (desc_chain.head_index(), len);
used_count += 1;
self.queue
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
self.queue.add_used(mem.deref(), desc_index, len).unwrap();
}
used_count > 0
used_descs
}
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {

View File

@ -15,7 +15,6 @@ use crate::{VirtioInterrupt, VirtioInterruptType};
use seccompiler::SeccompAction;
use std::fs::File;
use std::io;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::AtomicBool;
@ -50,8 +49,7 @@ impl RngEpollHandler {
fn process_queue(&mut self) -> bool {
let queue = &mut self.queue;
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
let desc = desc_chain.next().unwrap();
let mut len = 0;
@ -73,15 +71,13 @@ impl RngEpollHandler {
}
}
used_desc_heads[used_count] = (desc_chain.head_index(), len);
used_count += 1;
queue
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
.unwrap();
used_descs = true;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
queue.add_used(mem.deref(), desc_index, len).unwrap();
}
used_count > 0
used_descs
}
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {

View File

@ -40,7 +40,6 @@ use crate::{
use byteorder::{ByteOrder, LittleEndian};
use seccompiler::SeccompAction;
use std::io;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::result;
@ -125,8 +124,7 @@ where
fn process_rx(&mut self) -> result::Result<(), DeviceError> {
debug!("vsock: epoll_handler::process_rx()");
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = self.queues[0].pop_descriptor_chain(self.mem.memory()) {
let used_len = match VsockPacket::from_rx_virtq_head(
@ -149,18 +147,13 @@ where
}
};
used_desc_heads[used_count] = (desc_chain.head_index(), used_len);
used_count += 1;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
self.queues[0]
.add_used(mem.deref(), desc_index, len)
.add_used(desc_chain.memory(), desc_chain.head_index(), used_len)
.map_err(DeviceError::QueueAddUsed)?;
used_descs = true;
}
if used_count > 0 {
if used_descs {
self.signal_used_queue(0)
} else {
Ok(())
@ -173,8 +166,7 @@ where
fn process_tx(&mut self) -> result::Result<(), DeviceError> {
debug!("vsock: epoll_handler::process_tx()");
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
let mut used_count = 0;
let mut used_descs = false;
while let Some(mut desc_chain) = self.queues[1].pop_descriptor_chain(self.mem.memory()) {
let pkt = match VsockPacket::from_tx_virtq_head(
@ -184,8 +176,10 @@ where
Ok(pkt) => pkt,
Err(e) => {
error!("vsock: error reading TX packet: {:?}", e);
used_desc_heads[used_count] = (desc_chain.head_index(), 0);
used_count += 1;
self.queues[1]
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
.map_err(DeviceError::QueueAddUsed)?;
used_descs = true;
continue;
}
};
@ -195,18 +189,13 @@ where
break;
}
used_desc_heads[used_count] = (desc_chain.head_index(), 0);
used_count += 1;
}
let mem = self.mem.memory();
for &(desc_index, len) in &used_desc_heads[..used_count] {
self.queues[1]
.add_used(mem.deref(), desc_index, len)
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
.map_err(DeviceError::QueueAddUsed)?;
used_descs = true;
}
if used_count > 0 {
if used_descs {
self.signal_used_queue(1)
} else {
Ok(())