2021-05-17 20:21:34 +00:00
|
|
|
// Copyright (c) 2021 Intel Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
|
2021-06-02 19:08:04 +00:00
|
|
|
use crate::GuestMemoryMmap;
|
2021-05-17 20:21:34 +00:00
|
|
|
use crate::Tap;
|
|
|
|
use libc::c_uint;
|
2022-01-26 16:12:15 +00:00
|
|
|
use std::sync::Arc;
|
2021-05-17 20:21:34 +00:00
|
|
|
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,
|
|
|
|
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_GUEST_CSUM,
|
|
|
|
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
|
|
|
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
|
|
|
|
};
|
2022-01-26 16:33:36 +00:00
|
|
|
use virtio_queue::Queue;
|
2022-01-26 16:12:15 +00:00
|
|
|
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
|
2022-01-26 16:33:36 +00:00
|
|
|
use vm_virtio::AccessPlatform;
|
2021-05-17 20:21:34 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
/// Read queue failed.
|
|
|
|
GuestMemory(GuestMemoryError),
|
2021-10-21 10:41:16 +00:00
|
|
|
/// No control header descriptor
|
|
|
|
NoControlHeaderDescriptor,
|
2021-10-21 16:02:21 +00:00
|
|
|
/// Missing the data descriptor in the chain.
|
|
|
|
NoDataDescriptor,
|
2021-05-17 20:21:34 +00:00
|
|
|
/// No status descriptor
|
|
|
|
NoStatusDescriptor,
|
2021-10-21 10:41:16 +00:00
|
|
|
/// Failed adding used index
|
|
|
|
QueueAddUsed(virtio_queue::Error),
|
|
|
|
/// Failed creating an iterator over the queue
|
|
|
|
QueueIterator(virtio_queue::Error),
|
|
|
|
/// Failed enabling notification for the queue
|
|
|
|
QueueEnableNotification(virtio_queue::Error),
|
2021-05-17 20:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Result<T> = std::result::Result<T, Error>;
|
|
|
|
|
|
|
|
#[repr(C, packed)]
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
|
|
pub struct ControlHeader {
|
|
|
|
pub class: u8,
|
|
|
|
pub cmd: u8,
|
|
|
|
}
|
|
|
|
|
2021-11-17 13:28:57 +00:00
|
|
|
// SAFETY: ControlHeader only contains a series of integers
|
2021-05-17 20:21:34 +00:00
|
|
|
unsafe impl ByteValued for ControlHeader {}
|
|
|
|
|
|
|
|
pub struct CtrlQueue {
|
|
|
|
pub taps: Vec<Tap>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CtrlQueue {
|
|
|
|
pub fn new(taps: Vec<Tap>) -> Self {
|
|
|
|
CtrlQueue { taps }
|
|
|
|
}
|
|
|
|
|
2021-10-21 10:41:16 +00:00
|
|
|
pub fn process(
|
|
|
|
&mut self,
|
|
|
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
2022-01-26 16:12:15 +00:00
|
|
|
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
2021-10-21 10:41:16 +00:00
|
|
|
) -> Result<bool> {
|
2021-05-17 20:21:34 +00:00
|
|
|
let mut used_desc_heads = Vec::new();
|
2021-10-21 10:41:16 +00:00
|
|
|
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
|
|
|
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
|
|
|
|
2022-01-26 16:12:15 +00:00
|
|
|
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()
|
|
|
|
};
|
|
|
|
|
2021-10-21 10:41:16 +00:00
|
|
|
let ctrl_hdr: ControlHeader = desc_chain
|
|
|
|
.memory()
|
2022-01-26 16:12:15 +00:00
|
|
|
.read_obj(ctrl_desc_addr)
|
2021-10-21 10:41:16 +00:00
|
|
|
.map_err(Error::GuestMemory)?;
|
2021-10-21 16:02:21 +00:00
|
|
|
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
2022-01-26 16:12:15 +00:00
|
|
|
|
|
|
|
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()
|
|
|
|
};
|
|
|
|
|
2021-10-21 10:41:16 +00:00
|
|
|
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
2021-05-17 20:21:34 +00:00
|
|
|
|
2022-01-26 16:12:15 +00:00
|
|
|
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()
|
|
|
|
};
|
|
|
|
|
2021-05-17 20:21:34 +00:00
|
|
|
let ok = match u32::from(ctrl_hdr.class) {
|
|
|
|
VIRTIO_NET_CTRL_MQ => {
|
2021-10-21 10:41:16 +00:00
|
|
|
let queue_pairs = desc_chain
|
|
|
|
.memory()
|
2022-01-26 16:12:15 +00:00
|
|
|
.read_obj::<u16>(data_desc_addr)
|
2021-05-17 20:21:34 +00:00
|
|
|
.map_err(Error::GuestMemory)?;
|
|
|
|
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
|
|
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
|
|
|
false
|
|
|
|
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
|
|
|
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
|
|
|
{
|
|
|
|
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
info!("Number of MQ pairs requested: {}", queue_pairs);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
2021-10-21 10:41:16 +00:00
|
|
|
let features = desc_chain
|
|
|
|
.memory()
|
2022-01-26 16:12:15 +00:00
|
|
|
.read_obj::<u64>(data_desc_addr)
|
2021-05-17 20:21:34 +00:00
|
|
|
.map_err(Error::GuestMemory)?;
|
|
|
|
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
|
|
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
let mut ok = true;
|
|
|
|
for tap in self.taps.iter_mut() {
|
|
|
|
info!("Reprogramming tap offload with features: {}", features);
|
|
|
|
tap.set_offload(virtio_features_to_tap_offload(features))
|
|
|
|
.map_err(|e| {
|
|
|
|
error!("Error programming tap offload: {:?}", e);
|
|
|
|
ok = false
|
|
|
|
})
|
|
|
|
.ok();
|
|
|
|
}
|
|
|
|
ok
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
warn!("Unsupported command {:?}", ctrl_hdr);
|
|
|
|
false
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-10-21 10:41:16 +00:00
|
|
|
desc_chain
|
|
|
|
.memory()
|
|
|
|
.write_obj(
|
|
|
|
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
2022-01-26 16:12:15 +00:00
|
|
|
status_desc_addr,
|
2021-10-21 10:41:16 +00:00
|
|
|
)
|
|
|
|
.map_err(Error::GuestMemory)?;
|
|
|
|
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
|
|
|
used_desc_heads.push((desc_chain.head_index(), len));
|
2021-05-17 20:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (desc_index, len) in used_desc_heads.iter() {
|
2021-10-21 10:41:16 +00:00
|
|
|
queue
|
|
|
|
.add_used(*desc_index, *len)
|
|
|
|
.map_err(Error::QueueAddUsed)?;
|
|
|
|
queue
|
|
|
|
.enable_notification()
|
|
|
|
.map_err(Error::QueueEnableNotification)?;
|
2021-05-17 20:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(!used_desc_heads.is_empty())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
|
|
|
|
let mut tap_offloads: c_uint = 0;
|
|
|
|
if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
|
|
|
|
tap_offloads |= net_gen::TUN_F_CSUM;
|
|
|
|
}
|
|
|
|
if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
|
|
|
|
tap_offloads |= net_gen::TUN_F_TSO4;
|
|
|
|
}
|
|
|
|
if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
|
|
|
|
tap_offloads |= net_gen::TUN_F_TSO6;
|
|
|
|
}
|
|
|
|
if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
|
|
|
|
tap_offloads |= net_gen::TUN_F_TSO_ECN;
|
|
|
|
}
|
|
|
|
if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
|
|
|
|
tap_offloads |= net_gen::TUN_F_UFO;
|
|
|
|
}
|
|
|
|
|
|
|
|
tap_offloads
|
|
|
|
}
|