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