xref: /cloud-hypervisor/net_util/src/ctrl_queue.rs (revision 9af2968a7dc47b89bf07ea9dc5e735084efcfa3a)
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