1 // Copyright (c) 2021 Intel Corporation. All rights reserved. 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use std::sync::Arc; 6 7 use thiserror::Error; 8 use virtio_bindings::virtio_net::{ 9 VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ, 10 VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, 11 VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_OK, 12 }; 13 use virtio_queue::{Queue, QueueT}; 14 use vm_memory::{ByteValued, Bytes, GuestMemoryError}; 15 use vm_virtio::{AccessPlatform, Translatable}; 16 17 use super::virtio_features_to_tap_offload; 18 use crate::{GuestMemoryMmap, Tap}; 19 20 #[derive(Error, Debug)] 21 pub enum Error { 22 /// Read queue failed. 23 #[error("Read queue failed")] 24 GuestMemory(#[source] GuestMemoryError), 25 /// No control header descriptor 26 #[error("No control header descriptor")] 27 NoControlHeaderDescriptor, 28 /// Missing the data descriptor in the chain. 29 #[error("Missing the data descriptor in the chain")] 30 NoDataDescriptor, 31 /// No status descriptor 32 #[error("No status descriptor")] 33 NoStatusDescriptor, 34 /// Failed adding used index 35 #[error("Failed adding used index")] 36 QueueAddUsed(#[source] virtio_queue::Error), 37 /// Failed creating an iterator over the queue 38 #[error("Failed creating an iterator over the queue")] 39 QueueIterator(#[source] virtio_queue::Error), 40 /// Failed enabling notification for the queue 41 #[error("Failed enabling notification for the queue")] 42 QueueEnableNotification(#[source] virtio_queue::Error), 43 } 44 45 type Result<T> = std::result::Result<T, Error>; 46 47 #[repr(C, packed)] 48 #[derive(Debug, Clone, Copy, Default)] 49 pub struct ControlHeader { 50 pub class: u8, 51 pub cmd: u8, 52 } 53 54 // SAFETY: ControlHeader only contains a series of integers 55 unsafe impl ByteValued for ControlHeader {} 56 57 pub struct CtrlQueue { 58 pub taps: Vec<Tap>, 59 } 60 61 impl CtrlQueue { 62 pub fn new(taps: Vec<Tap>) -> Self { 63 CtrlQueue { taps } 64 } 65 66 pub fn process( 67 &mut self, 68 mem: &GuestMemoryMmap, 69 queue: &mut Queue, 70 access_platform: Option<&Arc<dyn AccessPlatform>>, 71 ) -> Result<()> { 72 while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) { 73 let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?; 74 75 let ctrl_hdr: ControlHeader = desc_chain 76 .memory() 77 .read_obj( 78 ctrl_desc 79 .addr() 80 .translate_gva(access_platform, ctrl_desc.len() as usize), 81 ) 82 .map_err(Error::GuestMemory)?; 83 let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?; 84 85 let data_desc_addr = data_desc 86 .addr() 87 .translate_gva(access_platform, data_desc.len() as usize); 88 89 let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?; 90 91 let ok = match u32::from(ctrl_hdr.class) { 92 VIRTIO_NET_CTRL_MQ => { 93 let queue_pairs = desc_chain 94 .memory() 95 .read_obj::<u16>(data_desc_addr) 96 .map_err(Error::GuestMemory)?; 97 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { 98 warn!("Unsupported command: {}", ctrl_hdr.cmd); 99 false 100 } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) 101 || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) 102 { 103 warn!("Number of MQ pairs out of range: {}", queue_pairs); 104 false 105 } else { 106 info!("Number of MQ pairs requested: {}", queue_pairs); 107 true 108 } 109 } 110 VIRTIO_NET_CTRL_GUEST_OFFLOADS => { 111 let features = desc_chain 112 .memory() 113 .read_obj::<u64>(data_desc_addr) 114 .map_err(Error::GuestMemory)?; 115 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { 116 warn!("Unsupported command: {}", ctrl_hdr.cmd); 117 false 118 } else { 119 let mut ok = true; 120 for tap in self.taps.iter_mut() { 121 info!("Reprogramming tap offload with features: {}", features); 122 tap.set_offload(virtio_features_to_tap_offload(features)) 123 .map_err(|e| { 124 error!("Error programming tap offload: {:?}", e); 125 ok = false 126 }) 127 .ok(); 128 } 129 ok 130 } 131 } 132 _ => { 133 warn!("Unsupported command {:?}", ctrl_hdr); 134 false 135 } 136 }; 137 138 desc_chain 139 .memory() 140 .write_obj( 141 if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, 142 status_desc 143 .addr() 144 .translate_gva(access_platform, status_desc.len() as usize), 145 ) 146 .map_err(Error::GuestMemory)?; 147 let len = ctrl_desc.len() + data_desc.len() + status_desc.len(); 148 149 queue 150 .add_used(desc_chain.memory(), desc_chain.head_index(), len) 151 .map_err(Error::QueueAddUsed)?; 152 153 if !queue 154 .enable_notification(mem) 155 .map_err(Error::QueueEnableNotification)? 156 { 157 break; 158 } 159 } 160 161 Ok(()) 162 } 163 } 164