From 40dc3e7c10dad185cc50e320a6d810e276543cbb Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Mon, 17 May 2021 22:21:34 +0200 Subject: [PATCH] net_util: Introduce virtio-net control queue handling code This code is ported from the net_util.rs in virtio-devices. The point being to move it to the net_util crate so that it can later be reused from vhost-user-net backend. Signed-off-by: Sebastien Boeuf --- net_util/src/ctrl_queue.rs | 140 +++++++++++++++++++++++++++++++++++++ net_util/src/lib.rs | 2 + 2 files changed, 142 insertions(+) create mode 100644 net_util/src/ctrl_queue.rs diff --git a/net_util/src/ctrl_queue.rs b/net_util/src/ctrl_queue.rs new file mode 100644 index 000000000..30e3b4d4b --- /dev/null +++ b/net_util/src/ctrl_queue.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2021 Intel Corporation. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +use crate::Tap; +use libc::c_uint; +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, +}; +use vm_memory::{ByteValued, Bytes, GuestMemoryError, GuestMemoryMmap}; +use vm_virtio::Queue; + +#[derive(Debug)] +pub enum Error { + /// Read queue failed. + GuestMemory(GuestMemoryError), + /// No queue pairs number. + NoQueuePairsDescriptor, + /// No status descriptor + NoStatusDescriptor, +} + +type Result = std::result::Result; + +#[repr(C, packed)] +#[derive(Debug, Clone, Copy, Default)] +pub struct ControlHeader { + pub class: u8, + pub cmd: u8, +} + +unsafe impl ByteValued for ControlHeader {} + +pub struct CtrlQueue { + pub taps: Vec, +} + +impl CtrlQueue { + pub fn new(taps: Vec) -> Self { + CtrlQueue { taps } + } + + pub fn process(&mut self, mem: &GuestMemoryMmap, queue: &mut Queue) -> Result { + let mut used_desc_heads = Vec::new(); + for avail_desc in queue.iter(&mem) { + let ctrl_hdr: ControlHeader = + mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?; + let data_desc = avail_desc + .next_descriptor() + .ok_or(Error::NoQueuePairsDescriptor)?; + let status_desc = data_desc + .next_descriptor() + .ok_or(Error::NoStatusDescriptor)?; + + let ok = match u32::from(ctrl_hdr.class) { + VIRTIO_NET_CTRL_MQ => { + let queue_pairs = mem + .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); + 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 = mem + .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); + 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 + } + }; + + mem.write_obj( + if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, + status_desc.addr, + ) + .map_err(Error::GuestMemory)?; + used_desc_heads.push((avail_desc.index, avail_desc.len)); + } + + for (desc_index, len) in used_desc_heads.iter() { + queue.add_used(&mem, *desc_index, *len); + queue.update_avail_event(&mem); + } + + 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 +} diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs index c9cc08dbe..99e34e667 100644 --- a/net_util/src/lib.rs +++ b/net_util/src/lib.rs @@ -13,6 +13,7 @@ extern crate lazy_static; #[macro_use] extern crate log; +mod ctrl_queue; mod mac; mod open_tap; mod queue_pair; @@ -22,6 +23,7 @@ use std::io::Error as IoError; use std::os::unix::io::{FromRawFd, RawFd}; use std::{io, mem, net}; +pub use ctrl_queue::{CtrlQueue, Error as CtrlQueueError}; pub use mac::{MacAddr, MAC_ADDR_LEN}; pub use open_tap::{open_tap, Error as OpenTapError}; pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio};