xref: /cloud-hypervisor/net_util/src/ctrl_queue.rs (revision 61e57e1cb149de03ae1e0b799b9e5ba9a4a63ace)
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 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_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 virtio_queue::{Queue, QueueT};
16 use vm_memory::{ByteValued, Bytes, GuestMemoryError};
17 use vm_virtio::{AccessPlatform, Translatable};
18 
19 use crate::{GuestMemoryMmap, Tap};
20 
21 #[derive(Debug)]
22 pub enum Error {
23     /// Read queue failed.
24     GuestMemory(GuestMemoryError),
25     /// No control header descriptor
26     NoControlHeaderDescriptor,
27     /// Missing the data descriptor in the chain.
28     NoDataDescriptor,
29     /// No status descriptor
30     NoStatusDescriptor,
31     /// Failed adding used index
32     QueueAddUsed(virtio_queue::Error),
33     /// Failed creating an iterator over the queue
34     QueueIterator(virtio_queue::Error),
35     /// Failed enabling notification for the queue
36     QueueEnableNotification(virtio_queue::Error),
37 }
38 
39 type Result<T> = std::result::Result<T, Error>;
40 
41 #[repr(C, packed)]
42 #[derive(Debug, Clone, Copy, Default)]
43 pub struct ControlHeader {
44     pub class: u8,
45     pub cmd: u8,
46 }
47 
48 // SAFETY: ControlHeader only contains a series of integers
49 unsafe impl ByteValued for ControlHeader {}
50 
51 pub struct CtrlQueue {
52     pub taps: Vec<Tap>,
53 }
54 
55 impl CtrlQueue {
56     pub fn new(taps: Vec<Tap>) -> Self {
57         CtrlQueue { taps }
58     }
59 
60     pub fn process(
61         &mut self,
62         mem: &GuestMemoryMmap,
63         queue: &mut Queue,
64         access_platform: Option<&Arc<dyn AccessPlatform>>,
65     ) -> Result<()> {
66         while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
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 
143             queue
144                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
145                 .map_err(Error::QueueAddUsed)?;
146 
147             if !queue
148                 .enable_notification(mem)
149                 .map_err(Error::QueueEnableNotification)?
150             {
151                 break;
152             }
153         }
154 
155         Ok(())
156     }
157 }
158 
159 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
160     let mut tap_offloads: c_uint = 0;
161     if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
162         tap_offloads |= net_gen::TUN_F_CSUM;
163     }
164     if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
165         tap_offloads |= net_gen::TUN_F_TSO4;
166     }
167     if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
168         tap_offloads |= net_gen::TUN_F_TSO6;
169     }
170     if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
171         tap_offloads |= net_gen::TUN_F_TSO_ECN;
172     }
173     if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
174         tap_offloads |= net_gen::TUN_F_UFO;
175     }
176 
177     tap_offloads
178 }
179