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