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