xref: /cloud-hypervisor/net_util/src/ctrl_queue.rs (revision eea9bcea38e0c5649f444c829f3a4f9c22aa486c)
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, QueueT};
17 use vm_memory::{ByteValued, Bytes, 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         mem: &GuestMemoryMmap,
62         queue: &mut Queue,
63         access_platform: Option<&Arc<dyn AccessPlatform>>,
64     ) -> Result<()> {
65         while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
66             let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
67 
68             let ctrl_hdr: ControlHeader = desc_chain
69                 .memory()
70                 .read_obj(
71                     ctrl_desc
72                         .addr()
73                         .translate_gva(access_platform, ctrl_desc.len() as usize),
74                 )
75                 .map_err(Error::GuestMemory)?;
76             let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
77 
78             let data_desc_addr = data_desc
79                 .addr()
80                 .translate_gva(access_platform, data_desc.len() as usize);
81 
82             let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
83 
84             let ok = match u32::from(ctrl_hdr.class) {
85                 VIRTIO_NET_CTRL_MQ => {
86                     let queue_pairs = desc_chain
87                         .memory()
88                         .read_obj::<u16>(data_desc_addr)
89                         .map_err(Error::GuestMemory)?;
90                     if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
91                         warn!("Unsupported command: {}", ctrl_hdr.cmd);
92                         false
93                     } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
94                         || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
95                     {
96                         warn!("Number of MQ pairs out of range: {}", queue_pairs);
97                         false
98                     } else {
99                         info!("Number of MQ pairs requested: {}", queue_pairs);
100                         true
101                     }
102                 }
103                 VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
104                     let features = desc_chain
105                         .memory()
106                         .read_obj::<u64>(data_desc_addr)
107                         .map_err(Error::GuestMemory)?;
108                     if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
109                         warn!("Unsupported command: {}", ctrl_hdr.cmd);
110                         false
111                     } else {
112                         let mut ok = true;
113                         for tap in self.taps.iter_mut() {
114                             info!("Reprogramming tap offload with features: {}", features);
115                             tap.set_offload(virtio_features_to_tap_offload(features))
116                                 .map_err(|e| {
117                                     error!("Error programming tap offload: {:?}", e);
118                                     ok = false
119                                 })
120                                 .ok();
121                         }
122                         ok
123                     }
124                 }
125                 _ => {
126                     warn!("Unsupported command {:?}", ctrl_hdr);
127                     false
128                 }
129             };
130 
131             desc_chain
132                 .memory()
133                 .write_obj(
134                     if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
135                     status_desc
136                         .addr()
137                         .translate_gva(access_platform, status_desc.len() as usize),
138                 )
139                 .map_err(Error::GuestMemory)?;
140             let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
141 
142             queue
143                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
144                 .map_err(Error::QueueAddUsed)?;
145 
146             if !queue
147                 .enable_notification(mem)
148                 .map_err(Error::QueueEnableNotification)?
149             {
150                 break;
151             }
152         }
153 
154         Ok(())
155     }
156 }
157 
158 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
159     let mut tap_offloads: c_uint = 0;
160     if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
161         tap_offloads |= net_gen::TUN_F_CSUM;
162     }
163     if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
164         tap_offloads |= net_gen::TUN_F_TSO4;
165     }
166     if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
167         tap_offloads |= net_gen::TUN_F_TSO6;
168     }
169     if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
170         tap_offloads |= net_gen::TUN_F_TSO_ECN;
171     }
172     if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
173         tap_offloads |= net_gen::TUN_F_UFO;
174     }
175 
176     tap_offloads
177 }
178