xref: /cloud-hypervisor/net_util/src/ctrl_queue.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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 std::sync::Arc;
9 use virtio_bindings::bindings::virtio_net::{
10     VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
11     VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
12     VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_GUEST_CSUM,
13     VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
14     VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
15 };
16 use virtio_queue::Queue;
17 use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
18 use vm_virtio::{AccessPlatform, Translatable};
19 
20 #[derive(Debug)]
21 pub enum Error {
22     /// Read queue failed.
23     GuestMemory(GuestMemoryError),
24     /// No control header descriptor
25     NoControlHeaderDescriptor,
26     /// Missing the data descriptor in the chain.
27     NoDataDescriptor,
28     /// No status descriptor
29     NoStatusDescriptor,
30     /// Failed adding used index
31     QueueAddUsed(virtio_queue::Error),
32     /// Failed creating an iterator over the queue
33     QueueIterator(virtio_queue::Error),
34     /// Failed enabling notification for the queue
35     QueueEnableNotification(virtio_queue::Error),
36 }
37 
38 type Result<T> = std::result::Result<T, Error>;
39 
40 #[repr(C, packed)]
41 #[derive(Debug, Clone, Copy, Default)]
42 pub struct ControlHeader {
43     pub class: u8,
44     pub cmd: u8,
45 }
46 
47 // SAFETY: ControlHeader only contains a series of integers
48 unsafe impl ByteValued for ControlHeader {}
49 
50 pub struct CtrlQueue {
51     pub taps: Vec<Tap>,
52 }
53 
54 impl CtrlQueue {
55     pub fn new(taps: Vec<Tap>) -> Self {
56         CtrlQueue { taps }
57     }
58 
59     pub fn process(
60         &mut self,
61         queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
62         access_platform: Option<&Arc<dyn AccessPlatform>>,
63     ) -> Result<()> {
64         let mut used_desc_heads = Vec::new();
65         loop {
66             for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
67                 let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
68 
69                 let ctrl_hdr: ControlHeader = desc_chain
70                     .memory()
71                     .read_obj(
72                         ctrl_desc
73                             .addr()
74                             .translate_gva(access_platform, ctrl_desc.len() as usize),
75                     )
76                     .map_err(Error::GuestMemory)?;
77                 let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
78 
79                 let data_desc_addr = data_desc
80                     .addr()
81                     .translate_gva(access_platform, data_desc.len() as usize);
82 
83                 let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
84 
85                 let ok = match u32::from(ctrl_hdr.class) {
86                     VIRTIO_NET_CTRL_MQ => {
87                         let queue_pairs = desc_chain
88                             .memory()
89                             .read_obj::<u16>(data_desc_addr)
90                             .map_err(Error::GuestMemory)?;
91                         if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
92                             warn!("Unsupported command: {}", ctrl_hdr.cmd);
93                             false
94                         } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
95                             || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
96                         {
97                             warn!("Number of MQ pairs out of range: {}", queue_pairs);
98                             false
99                         } else {
100                             info!("Number of MQ pairs requested: {}", queue_pairs);
101                             true
102                         }
103                     }
104                     VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
105                         let features = desc_chain
106                             .memory()
107                             .read_obj::<u64>(data_desc_addr)
108                             .map_err(Error::GuestMemory)?;
109                         if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
110                             warn!("Unsupported command: {}", ctrl_hdr.cmd);
111                             false
112                         } else {
113                             let mut ok = true;
114                             for tap in self.taps.iter_mut() {
115                                 info!("Reprogramming tap offload with features: {}", features);
116                                 tap.set_offload(virtio_features_to_tap_offload(features))
117                                     .map_err(|e| {
118                                         error!("Error programming tap offload: {:?}", e);
119                                         ok = false
120                                     })
121                                     .ok();
122                             }
123                             ok
124                         }
125                     }
126                     _ => {
127                         warn!("Unsupported command {:?}", ctrl_hdr);
128                         false
129                     }
130                 };
131 
132                 desc_chain
133                     .memory()
134                     .write_obj(
135                         if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
136                         status_desc
137                             .addr()
138                             .translate_gva(access_platform, status_desc.len() as usize),
139                     )
140                     .map_err(Error::GuestMemory)?;
141                 let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
142                 used_desc_heads.push((desc_chain.head_index(), len));
143             }
144 
145             for (desc_index, len) in used_desc_heads.iter() {
146                 queue
147                     .add_used(*desc_index, *len)
148                     .map_err(Error::QueueAddUsed)?;
149             }
150 
151             if !queue
152                 .enable_notification()
153                 .map_err(Error::QueueEnableNotification)?
154             {
155                 break;
156             }
157         }
158 
159         Ok(())
160     }
161 }
162 
163 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
164     let mut tap_offloads: c_uint = 0;
165     if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
166         tap_offloads |= net_gen::TUN_F_CSUM;
167     }
168     if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
169         tap_offloads |= net_gen::TUN_F_TSO4;
170     }
171     if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
172         tap_offloads |= net_gen::TUN_F_TSO6;
173     }
174     if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
175         tap_offloads |= net_gen::TUN_F_TSO_ECN;
176     }
177     if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
178         tap_offloads |= net_gen::TUN_F_UFO;
179     }
180 
181     tap_offloads
182 }
183