From 4becb11a44ae295279c6a9a46289063555357545 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 26 Jan 2022 17:12:15 +0100 Subject: [PATCH] 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 --- net_util/src/ctrl_queue.rs | 45 ++++++++++++++++--- net_util/src/queue_pair.rs | 67 ++++++++++++++++++++++------ vhost_user_net/src/lib.rs | 1 + virtio-devices/src/net.rs | 14 +++++- virtio-devices/src/vhost_user/net.rs | 2 +- 5 files changed, 106 insertions(+), 23 deletions(-) diff --git a/net_util/src/ctrl_queue.rs b/net_util/src/ctrl_queue.rs index 41161c6bf..745b3f8e7 100644 --- a/net_util/src/ctrl_queue.rs +++ b/net_util/src/ctrl_queue.rs @@ -5,6 +5,7 @@ use crate::GuestMemoryMmap; use crate::Tap; use libc::c_uint; +use std::sync::Arc; use virtio_bindings::bindings::virtio_net::{ 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, @@ -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_UFO, VIRTIO_NET_OK, }; -use virtio_queue::Queue; -use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError}; +use virtio_queue::{AccessPlatform, Queue}; +use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError}; #[derive(Debug)] pub enum Error { @@ -57,23 +58,55 @@ impl CtrlQueue { pub fn process( &mut self, queue: &mut Queue>, + access_platform: Option<&Arc>, ) -> Result { let mut used_desc_heads = Vec::new(); for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? { 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 .memory() - .read_obj(ctrl_desc.addr()) + .read_obj(ctrl_desc_addr) .map_err(Error::GuestMemory)?; 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_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) { VIRTIO_NET_CTRL_MQ => { let queue_pairs = desc_chain .memory() - .read_obj::(data_desc.addr()) + .read_obj::(data_desc_addr) .map_err(Error::GuestMemory)?; if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { warn!("Unsupported command: {}", ctrl_hdr.cmd); @@ -91,7 +124,7 @@ impl CtrlQueue { VIRTIO_NET_CTRL_GUEST_OFFLOADS => { let features = desc_chain .memory() - .read_obj::(data_desc.addr()) + .read_obj::(data_desc_addr) .map_err(Error::GuestMemory)?; if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { warn!("Unsupported command: {}", ctrl_hdr.cmd); @@ -120,7 +153,7 @@ impl CtrlQueue { .memory() .write_obj( if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, - status_desc.addr(), + status_desc_addr, ) .map_err(Error::GuestMemory)?; let len = ctrl_desc.len() + data_desc.len() + status_desc.len(); diff --git a/net_util/src/queue_pair.rs b/net_util/src/queue_pair.rs index 906fdbf5f..643defe23 100644 --- a/net_util/src/queue_pair.rs +++ b/net_util/src/queue_pair.rs @@ -10,8 +10,8 @@ use std::num::Wrapping; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use virtio_queue::Queue; -use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic}; +use virtio_queue::{AccessPlatform, Queue}; +use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryAtomic}; #[derive(Clone)] pub struct TxVirtio { @@ -38,6 +38,7 @@ impl TxVirtio { tap: &mut Tap, queue: &mut Queue>, rate_limiter: &mut Option, + access_platform: Option<&Arc>, ) -> Result { let mut retry_write = false; let mut rate_limit_reached = false; @@ -58,10 +59,20 @@ impl TxVirtio { let mut iovecs = Vec::new(); 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 { let buf = desc_chain .memory() - .get_slice(desc.addr(), desc.len() as usize) + .get_slice(desc_addr, desc.len() as usize) .map_err(NetQueuePairError::GuestMemory)? .as_ptr(); let iovec = libc::iovec { @@ -72,7 +83,7 @@ impl TxVirtio { } else { error!( "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}", - desc.addr().0, + desc_addr.0, desc.len(), desc.is_write_only() ); @@ -161,6 +172,7 @@ impl RxVirtio { tap: &mut Tap, queue: &mut Queue>, rate_limiter: &mut Option, + access_platform: Option<&Arc>, ) -> Result { let mut exhausted_descs = true; let mut rate_limit_reached = false; @@ -181,15 +193,36 @@ impl RxVirtio { let desc = desc_chain .next() .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 iovecs = Vec::new(); 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 { let buf = desc_chain .memory() - .get_slice(desc.addr(), desc.len() as usize) + .get_slice(desc_addr, desc.len() as usize) .map_err(NetQueuePairError::GuestMemory)? .as_ptr(); let iovec = libc::iovec { @@ -200,7 +233,7 @@ impl RxVirtio { } else { error!( "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}", - desc.addr().0, + desc_addr.0, desc.len(), desc.is_write_only() ); @@ -326,6 +359,7 @@ pub struct NetQueuePair { pub rx_desc_avail: bool, pub rx_rate_limiter: Option, pub tx_rate_limiter: Option, + pub access_platform: Option>, } impl NetQueuePair { @@ -333,9 +367,12 @@ impl NetQueuePair { &mut self, queue: &mut Queue>, ) -> Result { - let tx_tap_retry = - self.tx - .process_desc_chain(&mut self.tap, queue, &mut self.tx_rate_limiter)?; + let tx_tap_retry = self.tx.process_desc_chain( + &mut self.tap, + 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 if tx_tap_retry && !self.tx_tap_listening { @@ -378,10 +415,12 @@ impl NetQueuePair { &mut self, queue: &mut Queue>, ) -> Result { - self.rx_desc_avail = - !self - .rx - .process_desc_chain(&mut self.tap, queue, &mut self.rx_rate_limiter)?; + self.rx_desc_avail = !self.rx.process_desc_chain( + &mut self.tap, + queue, + &mut self.rx_rate_limiter, + self.access_platform.as_ref(), + )?; let rate_limit_reached = self .rx_rate_limiter .as_ref() diff --git a/vhost_user_net/src/lib.rs b/vhost_user_net/src/lib.rs index c5bc4f58b..7e774862f 100644 --- a/vhost_user_net/src/lib.rs +++ b/vhost_user_net/src/lib.rs @@ -93,6 +93,7 @@ impl VhostUserNetThread { rx_desc_avail: false, rx_rate_limiter: None, tx_rate_limiter: None, + access_platform: None, }, }) } diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index 7bca69133..ec5920612 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -35,7 +35,7 @@ use versionize::{VersionMap, Versionize, VersionizeResult}; use versionize_derive::Versionize; use virtio_bindings::bindings::virtio_net::*; 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_migration::VersionMapped; use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; @@ -51,6 +51,7 @@ pub struct NetCtrlEpollHandler { pub ctrl_q: CtrlQueue, pub queue_evt: EventFd, pub queue: Queue>, + pub access_platform: Option>, } impl NetCtrlEpollHandler { @@ -76,7 +77,10 @@ impl EpollHelperHandler for NetCtrlEpollHandler { error!("failed to get ctl queue event: {:?}", e); 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); return true; } @@ -572,6 +576,7 @@ impl VirtioDevice for Net { ctrl_q: CtrlQueue::new(self.taps.clone()), queue: cvq_queue, queue_evt: cvq_queue_evt, + access_platform: self.common.access_platform.clone(), }; let paused = self.common.paused.clone(); @@ -648,6 +653,7 @@ impl VirtioDevice for Net { rx_desc_avail: false, rx_rate_limiter, tx_rate_limiter, + access_platform: self.common.access_platform.clone(), }, queue_index_base: (i * 2) as u16, queue_pair, @@ -709,6 +715,10 @@ impl VirtioDevice for Net { Some(counters) } + + fn set_access_platform(&mut self, access_platform: Arc) { + self.common.set_access_platform(access_platform) + } } impl Pausable for Net { diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index 451f88674..08a8d92d7 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -89,7 +89,7 @@ impl EpollHelperHandler for NetCtrlEpollHandler { error!("failed to get ctl queue event: {:?}", e); 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); return true; }