1 // Copyright (c) 2021 Intel Corporation. All rights reserved. 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use crate::GuestMemoryMmap; 6 use crate::Tap; 7 use libc::c_uint; 8 use virtio_bindings::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_F_GUEST_CSUM, 12 VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, 13 VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK, 14 }; 15 use vm_memory::{ByteValued, Bytes, GuestMemoryError}; 16 use vm_virtio::Queue; 17 18 #[derive(Debug)] 19 pub enum Error { 20 /// Read queue failed. 21 GuestMemory(GuestMemoryError), 22 /// No queue pairs number. 23 NoQueuePairsDescriptor, 24 /// No status descriptor 25 NoStatusDescriptor, 26 } 27 28 type Result<T> = std::result::Result<T, Error>; 29 30 #[repr(C, packed)] 31 #[derive(Debug, Clone, Copy, Default)] 32 pub struct ControlHeader { 33 pub class: u8, 34 pub cmd: u8, 35 } 36 37 unsafe impl ByteValued for ControlHeader {} 38 39 pub struct CtrlQueue { 40 pub taps: Vec<Tap>, 41 } 42 43 impl CtrlQueue { 44 pub fn new(taps: Vec<Tap>) -> Self { 45 CtrlQueue { taps } 46 } 47 48 pub fn process(&mut self, mem: &GuestMemoryMmap, queue: &mut Queue) -> Result<bool> { 49 let mut used_desc_heads = Vec::new(); 50 for avail_desc in queue.iter(mem) { 51 let ctrl_hdr: ControlHeader = 52 mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?; 53 let data_desc = avail_desc 54 .next_descriptor() 55 .ok_or(Error::NoQueuePairsDescriptor)?; 56 let status_desc = data_desc 57 .next_descriptor() 58 .ok_or(Error::NoStatusDescriptor)?; 59 60 let ok = match u32::from(ctrl_hdr.class) { 61 VIRTIO_NET_CTRL_MQ => { 62 let queue_pairs = mem 63 .read_obj::<u16>(data_desc.addr) 64 .map_err(Error::GuestMemory)?; 65 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { 66 warn!("Unsupported command: {}", ctrl_hdr.cmd); 67 false 68 } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) 69 || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) 70 { 71 warn!("Number of MQ pairs out of range: {}", queue_pairs); 72 false 73 } else { 74 info!("Number of MQ pairs requested: {}", queue_pairs); 75 true 76 } 77 } 78 VIRTIO_NET_CTRL_GUEST_OFFLOADS => { 79 let features = mem 80 .read_obj::<u64>(data_desc.addr) 81 .map_err(Error::GuestMemory)?; 82 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { 83 warn!("Unsupported command: {}", ctrl_hdr.cmd); 84 false 85 } else { 86 let mut ok = true; 87 for tap in self.taps.iter_mut() { 88 info!("Reprogramming tap offload with features: {}", features); 89 tap.set_offload(virtio_features_to_tap_offload(features)) 90 .map_err(|e| { 91 error!("Error programming tap offload: {:?}", e); 92 ok = false 93 }) 94 .ok(); 95 } 96 ok 97 } 98 } 99 _ => { 100 warn!("Unsupported command {:?}", ctrl_hdr); 101 false 102 } 103 }; 104 105 mem.write_obj( 106 if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, 107 status_desc.addr, 108 ) 109 .map_err(Error::GuestMemory)?; 110 used_desc_heads.push((avail_desc.index, avail_desc.len)); 111 } 112 113 for (desc_index, len) in used_desc_heads.iter() { 114 queue.add_used(mem, *desc_index, *len); 115 queue.update_avail_event(mem); 116 } 117 118 Ok(!used_desc_heads.is_empty()) 119 } 120 } 121 122 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint { 123 let mut tap_offloads: c_uint = 0; 124 if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 { 125 tap_offloads |= net_gen::TUN_F_CSUM; 126 } 127 if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 { 128 tap_offloads |= net_gen::TUN_F_TSO4; 129 } 130 if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 { 131 tap_offloads |= net_gen::TUN_F_TSO6; 132 } 133 if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 { 134 tap_offloads |= net_gen::TUN_F_TSO_ECN; 135 } 136 if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 { 137 tap_offloads |= net_gen::TUN_F_UFO; 138 } 139 140 tap_offloads 141 } 142