mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-03 03:15:20 +00:00
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:
parent
ce984b73f5
commit
4becb11a44
@ -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();
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user