mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Import the std modules used across the crate instead of spelling the full paths at every use site. Signed-off-by: Henry Hrvoje Tonkovac <htonkovac@gmail.com> Assisted-by: Claude:Opus-4.8
196 lines
7.4 KiB
Rust
196 lines
7.4 KiB
Rust
// Copyright (c) 2021 Intel Corporation. All rights reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
use std::result;
|
|
|
|
use log::{debug, error, info, warn};
|
|
use thiserror::Error;
|
|
use virtio_bindings::virtio_net::{
|
|
VIRTIO_NET_CTRL_ANNOUNCE, VIRTIO_NET_CTRL_ANNOUNCE_ACK, 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_CTRL_RX,
|
|
VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST,
|
|
VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC,
|
|
VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR,
|
|
VIRTIO_NET_OK,
|
|
};
|
|
use virtio_queue::{Queue, QueueT};
|
|
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
|
|
use vm_virtio::{AccessPlatform, Translatable};
|
|
|
|
use super::virtio_features_to_tap_offload;
|
|
use crate::{GuestMemoryMmap, Tap};
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum Error {
|
|
/// Read queue failed.
|
|
#[error("Read queue failed")]
|
|
GuestMemory(#[source] GuestMemoryError),
|
|
/// No control header descriptor
|
|
#[error("No control header descriptor")]
|
|
NoControlHeaderDescriptor,
|
|
/// Missing the data descriptor in the chain.
|
|
#[error("Missing the data descriptor in the chain")]
|
|
NoDataDescriptor,
|
|
/// No status descriptor
|
|
#[error("No status descriptor")]
|
|
NoStatusDescriptor,
|
|
/// Failed adding used index
|
|
#[error("Failed adding used index")]
|
|
QueueAddUsed(#[source] virtio_queue::Error),
|
|
/// Failed creating an iterator over the queue
|
|
#[error("Failed creating an iterator over the queue")]
|
|
QueueIterator(#[source] virtio_queue::Error),
|
|
/// Failed enabling notification for the queue
|
|
#[error("Failed enabling notification for the queue")]
|
|
QueueEnableNotification(#[source] virtio_queue::Error),
|
|
}
|
|
|
|
type Result<T> = result::Result<T, Error>;
|
|
|
|
#[repr(C, packed)]
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub struct ControlHeader {
|
|
pub class: u8,
|
|
pub cmd: u8,
|
|
}
|
|
|
|
// SAFETY: ControlHeader only contains a series of integers
|
|
unsafe impl ByteValued for ControlHeader {}
|
|
|
|
fn is_tolerated_ctrl_command(ctrl_hdr: ControlHeader) -> bool {
|
|
match u32::from(ctrl_hdr.class) {
|
|
VIRTIO_NET_CTRL_RX => matches!(
|
|
u32::from(ctrl_hdr.cmd),
|
|
VIRTIO_NET_CTRL_RX_PROMISC
|
|
| VIRTIO_NET_CTRL_RX_ALLMULTI
|
|
| VIRTIO_NET_CTRL_RX_ALLUNI
|
|
| VIRTIO_NET_CTRL_RX_NOMULTI
|
|
| VIRTIO_NET_CTRL_RX_NOUNI
|
|
| VIRTIO_NET_CTRL_RX_NOBCAST
|
|
),
|
|
VIRTIO_NET_CTRL_VLAN => matches!(
|
|
u32::from(ctrl_hdr.cmd),
|
|
VIRTIO_NET_CTRL_VLAN_ADD | VIRTIO_NET_CTRL_VLAN_DEL
|
|
),
|
|
VIRTIO_NET_CTRL_ANNOUNCE => u32::from(ctrl_hdr.cmd) == VIRTIO_NET_CTRL_ANNOUNCE_ACK,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub struct CtrlQueue {
|
|
pub taps: Vec<Tap>,
|
|
}
|
|
|
|
impl CtrlQueue {
|
|
pub fn new(taps: Vec<Tap>) -> Self {
|
|
CtrlQueue { taps }
|
|
}
|
|
|
|
pub fn process(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
queue: &mut Queue,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
) -> Result<()> {
|
|
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
|
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
|
|
|
let ctrl_hdr: ControlHeader = desc_chain
|
|
.memory()
|
|
.read_obj(
|
|
ctrl_desc
|
|
.addr()
|
|
.translate_gva(access_platform, ctrl_desc.len() as usize)
|
|
.map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?,
|
|
)
|
|
.map_err(Error::GuestMemory)?;
|
|
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
|
|
|
let data_desc_addr = data_desc
|
|
.addr()
|
|
.translate_gva(access_platform, data_desc.len() as usize)
|
|
.map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?;
|
|
|
|
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
|
|
|
let ok = match u32::from(ctrl_hdr.class) {
|
|
VIRTIO_NET_CTRL_MQ => {
|
|
let queue_pairs = desc_chain
|
|
.memory()
|
|
.read_obj::<u16>(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);
|
|
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 => {
|
|
let features = desc_chain
|
|
.memory()
|
|
.read_obj::<u64>(data_desc_addr)
|
|
.map_err(Error::GuestMemory)?;
|
|
if u32::from(ctrl_hdr.cmd) == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
|
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
|
|
} else {
|
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
|
false
|
|
}
|
|
}
|
|
_ if is_tolerated_ctrl_command(ctrl_hdr) => {
|
|
debug!("Ignoring unsupported but tolerated control command {ctrl_hdr:?}");
|
|
true
|
|
}
|
|
_ => {
|
|
warn!("Unsupported command {ctrl_hdr:?}");
|
|
false
|
|
}
|
|
};
|
|
|
|
desc_chain
|
|
.memory()
|
|
.write_obj(
|
|
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
|
status_desc
|
|
.addr()
|
|
.translate_gva(access_platform, status_desc.len() as usize)
|
|
.map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?,
|
|
)
|
|
.map_err(Error::GuestMemory)?;
|
|
// Per the virtio spec the used length is bytes the device wrote
|
|
// to device-writable descriptors; here just the 1-byte ack.
|
|
queue
|
|
.add_used(desc_chain.memory(), desc_chain.head_index(), 1)
|
|
.map_err(Error::QueueAddUsed)?;
|
|
|
|
if !queue
|
|
.enable_notification(mem)
|
|
.map_err(Error::QueueEnableNotification)?
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|