virtio-devices: net: Handle descriptor address translation

Since we're trying to move away from the translation happening in the
virtio-queue crate, the device itself is performing the address
translation when needed.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-01-26 17:12:15 +01:00 committed by Rob Bradford
parent ce984b73f5
commit 4becb11a44
5 changed files with 106 additions and 23 deletions

View File

@ -5,6 +5,7 @@
use crate::GuestMemoryMmap; use crate::GuestMemoryMmap;
use crate::Tap; use crate::Tap;
use libc::c_uint; use libc::c_uint;
use std::sync::Arc;
use virtio_bindings::bindings::virtio_net::{ use virtio_bindings::bindings::virtio_net::{
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
@ -12,8 +13,8 @@ use virtio_bindings::bindings::virtio_net::{
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
}; };
use virtio_queue::Queue; use virtio_queue::{AccessPlatform, Queue};
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError}; use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -57,23 +58,55 @@ impl CtrlQueue {
pub fn process( pub fn process(
&mut self, &mut self,
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
access_platform: Option<&Arc<dyn AccessPlatform>>,
) -> Result<bool> { ) -> Result<bool> {
let mut used_desc_heads = Vec::new(); let mut used_desc_heads = Vec::new();
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? { for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?; let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
let ctrl_desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(ctrl_desc.addr().0, u64::from(ctrl_desc.len()))
.unwrap(),
)
} else {
ctrl_desc.addr()
};
let ctrl_hdr: ControlHeader = desc_chain let ctrl_hdr: ControlHeader = desc_chain
.memory() .memory()
.read_obj(ctrl_desc.addr()) .read_obj(ctrl_desc_addr)
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?; let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
let data_desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(data_desc.addr().0, u64::from(data_desc.len()))
.unwrap(),
)
} else {
data_desc.addr()
};
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?; let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
let status_desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(status_desc.addr().0, u64::from(status_desc.len()))
.unwrap(),
)
} else {
status_desc.addr()
};
let ok = match u32::from(ctrl_hdr.class) { let ok = match u32::from(ctrl_hdr.class) {
VIRTIO_NET_CTRL_MQ => { VIRTIO_NET_CTRL_MQ => {
let queue_pairs = desc_chain let queue_pairs = desc_chain
.memory() .memory()
.read_obj::<u16>(data_desc.addr()) .read_obj::<u16>(data_desc_addr)
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
warn!("Unsupported command: {}", ctrl_hdr.cmd); warn!("Unsupported command: {}", ctrl_hdr.cmd);
@ -91,7 +124,7 @@ impl CtrlQueue {
VIRTIO_NET_CTRL_GUEST_OFFLOADS => { VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
let features = desc_chain let features = desc_chain
.memory() .memory()
.read_obj::<u64>(data_desc.addr()) .read_obj::<u64>(data_desc_addr)
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
warn!("Unsupported command: {}", ctrl_hdr.cmd); warn!("Unsupported command: {}", ctrl_hdr.cmd);
@ -120,7 +153,7 @@ impl CtrlQueue {
.memory() .memory()
.write_obj( .write_obj(
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
status_desc.addr(), status_desc_addr,
) )
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
let len = ctrl_desc.len() + data_desc.len() + status_desc.len(); let len = ctrl_desc.len() + data_desc.len() + status_desc.len();

View File

@ -10,8 +10,8 @@ use std::num::Wrapping;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use virtio_queue::Queue; use virtio_queue::{AccessPlatform, Queue};
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic}; use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryAtomic};
#[derive(Clone)] #[derive(Clone)]
pub struct TxVirtio { pub struct TxVirtio {
@ -38,6 +38,7 @@ impl TxVirtio {
tap: &mut Tap, tap: &mut Tap,
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
rate_limiter: &mut Option<RateLimiter>, rate_limiter: &mut Option<RateLimiter>,
access_platform: Option<&Arc<dyn AccessPlatform>>,
) -> Result<bool, NetQueuePairError> { ) -> Result<bool, NetQueuePairError> {
let mut retry_write = false; let mut retry_write = false;
let mut rate_limit_reached = false; let mut rate_limit_reached = false;
@ -58,10 +59,20 @@ impl TxVirtio {
let mut iovecs = Vec::new(); let mut iovecs = Vec::new();
while let Some(desc) = next_desc { while let Some(desc) = next_desc {
let desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(desc.addr().0, u64::from(desc.len()))
.unwrap(),
)
} else {
desc.addr()
};
if !desc.is_write_only() && desc.len() > 0 { if !desc.is_write_only() && desc.len() > 0 {
let buf = desc_chain let buf = desc_chain
.memory() .memory()
.get_slice(desc.addr(), desc.len() as usize) .get_slice(desc_addr, desc.len() as usize)
.map_err(NetQueuePairError::GuestMemory)? .map_err(NetQueuePairError::GuestMemory)?
.as_ptr(); .as_ptr();
let iovec = libc::iovec { let iovec = libc::iovec {
@ -72,7 +83,7 @@ impl TxVirtio {
} else { } else {
error!( error!(
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}", "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
desc.addr().0, desc_addr.0,
desc.len(), desc.len(),
desc.is_write_only() desc.is_write_only()
); );
@ -161,6 +172,7 @@ impl RxVirtio {
tap: &mut Tap, tap: &mut Tap,
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
rate_limiter: &mut Option<RateLimiter>, rate_limiter: &mut Option<RateLimiter>,
access_platform: Option<&Arc<dyn AccessPlatform>>,
) -> Result<bool, NetQueuePairError> { ) -> Result<bool, NetQueuePairError> {
let mut exhausted_descs = true; let mut exhausted_descs = true;
let mut rate_limit_reached = false; let mut rate_limit_reached = false;
@ -181,15 +193,36 @@ impl RxVirtio {
let desc = desc_chain let desc = desc_chain
.next() .next()
.ok_or(NetQueuePairError::DescriptorChainTooShort)?; .ok_or(NetQueuePairError::DescriptorChainTooShort)?;
let num_buffers_addr = desc_chain.memory().checked_offset(desc.addr(), 10).unwrap();
let desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(desc.addr().0, u64::from(desc.len()))
.unwrap(),
)
} else {
desc.addr()
};
let num_buffers_addr = desc_chain.memory().checked_offset(desc_addr, 10).unwrap();
let mut next_desc = Some(desc); let mut next_desc = Some(desc);
let mut iovecs = Vec::new(); let mut iovecs = Vec::new();
while let Some(desc) = next_desc { while let Some(desc) = next_desc {
let desc_addr = if let Some(access_platform) = access_platform {
GuestAddress(
access_platform
.translate(desc.addr().0, u64::from(desc.len()))
.unwrap(),
)
} else {
desc.addr()
};
if desc.is_write_only() && desc.len() > 0 { if desc.is_write_only() && desc.len() > 0 {
let buf = desc_chain let buf = desc_chain
.memory() .memory()
.get_slice(desc.addr(), desc.len() as usize) .get_slice(desc_addr, desc.len() as usize)
.map_err(NetQueuePairError::GuestMemory)? .map_err(NetQueuePairError::GuestMemory)?
.as_ptr(); .as_ptr();
let iovec = libc::iovec { let iovec = libc::iovec {
@ -200,7 +233,7 @@ impl RxVirtio {
} else { } else {
error!( error!(
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}", "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
desc.addr().0, desc_addr.0,
desc.len(), desc.len(),
desc.is_write_only() desc.is_write_only()
); );
@ -326,6 +359,7 @@ pub struct NetQueuePair {
pub rx_desc_avail: bool, pub rx_desc_avail: bool,
pub rx_rate_limiter: Option<RateLimiter>, pub rx_rate_limiter: Option<RateLimiter>,
pub tx_rate_limiter: Option<RateLimiter>, pub tx_rate_limiter: Option<RateLimiter>,
pub access_platform: Option<Arc<dyn AccessPlatform>>,
} }
impl NetQueuePair { impl NetQueuePair {
@ -333,9 +367,12 @@ impl NetQueuePair {
&mut self, &mut self,
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
) -> Result<bool, NetQueuePairError> { ) -> Result<bool, NetQueuePairError> {
let tx_tap_retry = let tx_tap_retry = self.tx.process_desc_chain(
self.tx &mut self.tap,
.process_desc_chain(&mut self.tap, queue, &mut self.tx_rate_limiter)?; queue,
&mut self.tx_rate_limiter,
self.access_platform.as_ref(),
)?;
// We got told to try again when writing to the tap. Wait for the TAP to be writable // We got told to try again when writing to the tap. Wait for the TAP to be writable
if tx_tap_retry && !self.tx_tap_listening { if tx_tap_retry && !self.tx_tap_listening {
@ -378,10 +415,12 @@ impl NetQueuePair {
&mut self, &mut self,
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
) -> Result<bool, NetQueuePairError> { ) -> Result<bool, NetQueuePairError> {
self.rx_desc_avail = self.rx_desc_avail = !self.rx.process_desc_chain(
!self &mut self.tap,
.rx queue,
.process_desc_chain(&mut self.tap, queue, &mut self.rx_rate_limiter)?; &mut self.rx_rate_limiter,
self.access_platform.as_ref(),
)?;
let rate_limit_reached = self let rate_limit_reached = self
.rx_rate_limiter .rx_rate_limiter
.as_ref() .as_ref()

View File

@ -93,6 +93,7 @@ impl VhostUserNetThread {
rx_desc_avail: false, rx_desc_avail: false,
rx_rate_limiter: None, rx_rate_limiter: None,
tx_rate_limiter: None, tx_rate_limiter: None,
access_platform: None,
}, },
}) })
} }

View File

@ -35,7 +35,7 @@ use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize; use versionize_derive::Versionize;
use virtio_bindings::bindings::virtio_net::*; use virtio_bindings::bindings::virtio_net::*;
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
use virtio_queue::Queue; use virtio_queue::{AccessPlatform, Queue};
use vm_memory::{ByteValued, GuestMemoryAtomic}; use vm_memory::{ByteValued, GuestMemoryAtomic};
use vm_migration::VersionMapped; use vm_migration::VersionMapped;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
@ -51,6 +51,7 @@ pub struct NetCtrlEpollHandler {
pub ctrl_q: CtrlQueue, pub ctrl_q: CtrlQueue,
pub queue_evt: EventFd, pub queue_evt: EventFd,
pub queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>, pub queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
pub access_platform: Option<Arc<dyn AccessPlatform>>,
} }
impl NetCtrlEpollHandler { impl NetCtrlEpollHandler {
@ -76,7 +77,10 @@ impl EpollHelperHandler for NetCtrlEpollHandler {
error!("failed to get ctl queue event: {:?}", e); error!("failed to get ctl queue event: {:?}", e);
return true; return true;
} }
if let Err(e) = self.ctrl_q.process(&mut self.queue) { if let Err(e) = self
.ctrl_q
.process(&mut self.queue, self.access_platform.as_ref())
{
error!("failed to process ctrl queue: {:?}", e); error!("failed to process ctrl queue: {:?}", e);
return true; return true;
} }
@ -572,6 +576,7 @@ impl VirtioDevice for Net {
ctrl_q: CtrlQueue::new(self.taps.clone()), ctrl_q: CtrlQueue::new(self.taps.clone()),
queue: cvq_queue, queue: cvq_queue,
queue_evt: cvq_queue_evt, queue_evt: cvq_queue_evt,
access_platform: self.common.access_platform.clone(),
}; };
let paused = self.common.paused.clone(); let paused = self.common.paused.clone();
@ -648,6 +653,7 @@ impl VirtioDevice for Net {
rx_desc_avail: false, rx_desc_avail: false,
rx_rate_limiter, rx_rate_limiter,
tx_rate_limiter, tx_rate_limiter,
access_platform: self.common.access_platform.clone(),
}, },
queue_index_base: (i * 2) as u16, queue_index_base: (i * 2) as u16,
queue_pair, queue_pair,
@ -709,6 +715,10 @@ impl VirtioDevice for Net {
Some(counters) Some(counters)
} }
fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
self.common.set_access_platform(access_platform)
}
} }
impl Pausable for Net { impl Pausable for Net {

View File

@ -89,7 +89,7 @@ impl EpollHelperHandler for NetCtrlEpollHandler {
error!("failed to get ctl queue event: {:?}", e); error!("failed to get ctl queue event: {:?}", e);
return true; return true;
} }
if let Err(e) = self.ctrl_q.process(&mut self.queue) { if let Err(e) = self.ctrl_q.process(&mut self.queue, None) {
error!("failed to process ctrl queue: {:?}", e); error!("failed to process ctrl queue: {:?}", e);
return true; return true;
} }