// Copyright (c) 2019 Intel Corporation. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause use super::{ DescriptorChain, EpollHelper, EpollHelperError, EpollHelperHandler, Queue, EPOLL_HELPER_EVENT_LAST, }; use net_util::MacAddr; use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier}; use virtio_bindings::bindings::virtio_net::*; use vm_memory::{ ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError, GuestMemoryMmap, }; use vmm_sys_util::eventfd::EventFd; type Result = std::result::Result; const QUEUE_SIZE: usize = 256; // Event available on the control queue. const CTRL_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default, Deserialize)] pub struct VirtioNetConfig { pub mac: [u8; 6], pub status: u16, pub max_virtqueue_pairs: u16, pub mtu: u16, pub speed: u32, pub duplex: u8, } // We must explicitly implement Serialize since the structure is packed and // it's unsafe to borrow from a packed structure. And by default, if we derive // Serialize from serde, it will borrow the values from the structure. // That's why this implementation copies each field separately before it // serializes the entire structure field by field. impl Serialize for VirtioNetConfig { fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, { let mac = self.mac; let status = self.status; let max_virtqueue_pairs = self.max_virtqueue_pairs; let mtu = self.mtu; let speed = self.speed; let duplex = self.duplex; let mut virtio_net_config = serializer.serialize_struct("VirtioNetConfig", 17)?; virtio_net_config.serialize_field("mac", &mac)?; virtio_net_config.serialize_field("status", &status)?; virtio_net_config.serialize_field("max_virtqueue_pairs", &max_virtqueue_pairs)?; virtio_net_config.serialize_field("mtu", &mtu)?; virtio_net_config.serialize_field("speed", &speed)?; virtio_net_config.serialize_field("duplex", &duplex)?; virtio_net_config.end() } } // Safe because it only has data and has no implicit padding. unsafe impl ByteValued for VirtioNetConfig {} #[derive(Debug)] pub enum Error { /// Read process MQ. FailedProcessMq, /// Read queue failed. GuestMemory(GuestMemoryError), /// Invalid ctrl class InvalidCtlClass, /// Invalid ctrl command InvalidCtlCmd, /// Invalid descriptor InvalidDesc, /// Invalid queue pairs number InvalidQueuePairsNum, /// No memory passed in. NoMemory, /// No ueue pairs number. NoQueuePairsNum, } pub struct CtrlVirtio { pub queue_evt: EventFd, pub queue: Queue, } impl std::clone::Clone for CtrlVirtio { fn clone(&self) -> Self { CtrlVirtio { queue_evt: self.queue_evt.try_clone().unwrap(), queue: self.queue.clone(), } } } impl CtrlVirtio { pub fn new(queue: Queue, queue_evt: EventFd) -> Self { CtrlVirtio { queue_evt, queue } } fn process_mq(&self, mem: &GuestMemoryMmap, avail_desc: DescriptorChain) -> Result<()> { let mq_desc = if avail_desc.has_next() { avail_desc.next_descriptor().unwrap() } else { return Err(Error::NoQueuePairsNum); }; let queue_pairs = mem .read_obj::(mq_desc.addr) .map_err(Error::GuestMemory)?; if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) { return Err(Error::InvalidQueuePairsNum); } let status_desc = if mq_desc.has_next() { mq_desc.next_descriptor().unwrap() } else { return Err(Error::NoQueuePairsNum); }; mem.write_obj::(0, status_desc.addr) .map_err(Error::GuestMemory)?; Ok(()) } pub fn process_cvq(&mut self, mem: &GuestMemoryMmap) -> Result<()> { let mut used_desc_heads = [(0, 0); QUEUE_SIZE]; let mut used_count = 0; if let Some(avail_desc) = self.queue.iter(&mem).next() { used_desc_heads[used_count] = (avail_desc.index, avail_desc.len); used_count += 1; let ctrl_hdr = mem .read_obj::(avail_desc.addr) .map_err(Error::GuestMemory)?; let ctrl_hdr_v = ctrl_hdr.as_slice(); let class = ctrl_hdr_v[0]; let cmd = ctrl_hdr_v[1]; match u32::from(class) { VIRTIO_NET_CTRL_MQ => { if u32::from(cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { return Err(Error::InvalidCtlCmd); } if let Err(_e) = self.process_mq(&mem, avail_desc) { return Err(Error::FailedProcessMq); } } _ => return Err(Error::InvalidCtlClass), } } else { return Err(Error::InvalidDesc); } for &(desc_index, len) in &used_desc_heads[..used_count] { self.queue.add_used(&mem, desc_index, len); self.queue.update_avail_event(&mem); } Ok(()) } } pub struct NetCtrlEpollHandler { pub mem: GuestMemoryAtomic, pub kill_evt: EventFd, pub pause_evt: EventFd, pub ctrl_q: CtrlVirtio, pub epoll_fd: RawFd, } impl NetCtrlEpollHandler { pub fn run_ctrl( &mut self, paused: Arc, paused_sync: Arc, ) -> std::result::Result<(), EpollHelperError> { let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; helper.add_event(self.ctrl_q.queue_evt.as_raw_fd(), CTRL_QUEUE_EVENT)?; helper.run(paused, paused_sync, self)?; Ok(()) } } impl EpollHelperHandler for NetCtrlEpollHandler { fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool { let ev_type = event.data as u16; match ev_type { CTRL_QUEUE_EVENT => { let mem = self.mem.memory(); if let Err(e) = self.ctrl_q.queue_evt.read() { error!("failed to get ctl queue event: {:?}", e); return true; } if let Err(e) = self.ctrl_q.process_cvq(&mem) { error!("failed to process ctrl queue: {:?}", e); return true; } } _ => { error!("Unknown event for virtio-net"); return true; } } false } } pub fn build_net_config_space( mut config: &mut VirtioNetConfig, mac: MacAddr, num_queues: usize, mut avail_features: &mut u64, ) { config.mac.copy_from_slice(mac.get_bytes()); *avail_features |= 1 << VIRTIO_NET_F_MAC; build_net_config_space_with_mq(&mut config, num_queues, &mut avail_features); } pub fn build_net_config_space_with_mq( config: &mut VirtioNetConfig, num_queues: usize, avail_features: &mut u64, ) { let num_queue_pairs = (num_queues / 2) as u16; if (num_queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) && (num_queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) { config.max_virtqueue_pairs = num_queue_pairs; *avail_features |= 1 << VIRTIO_NET_F_MQ; } }