xref: /cloud-hypervisor/net_util/src/ctrl_queue.rs (revision 8338fa642fdd993d617e1103250d7f3dd3120e40)
140dc3e7cSSebastien Boeuf // Copyright (c) 2021 Intel Corporation. All rights reserved.
240dc3e7cSSebastien Boeuf //
340dc3e7cSSebastien Boeuf // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
440dc3e7cSSebastien Boeuf 
54becb11aSSebastien Boeuf use std::sync::Arc;
688a9f799SRob Bradford 
7ea6d5a04SPhilipp Schuster use thiserror::Error;
81d55de9cSdependabot[bot] use virtio_bindings::virtio_net::{
940dc3e7cSSebastien Boeuf     VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
1040dc3e7cSSebastien Boeuf     VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
11*8338fa64SHengqi Chen     VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_OK,
1240dc3e7cSSebastien Boeuf };
1387f57f7cSSebastien Boeuf use virtio_queue::{Queue, QueueT};
14a423bf13SSebastien Boeuf use vm_memory::{ByteValued, Bytes, GuestMemoryError};
1577df4e67SSebastien Boeuf use vm_virtio::{AccessPlatform, Translatable};
1640dc3e7cSSebastien Boeuf 
17*8338fa64SHengqi Chen use super::virtio_features_to_tap_offload;
1861e57e1cSRuoqing He use crate::{GuestMemoryMmap, Tap};
1988a9f799SRob Bradford 
20ea6d5a04SPhilipp Schuster #[derive(Error, Debug)]
2140dc3e7cSSebastien Boeuf pub enum Error {
2240dc3e7cSSebastien Boeuf     /// Read queue failed.
23ea6d5a04SPhilipp Schuster     #[error("Read queue failed")]
24ea6d5a04SPhilipp Schuster     GuestMemory(#[source] GuestMemoryError),
250249e864SSebastien Boeuf     /// No control header descriptor
26ea6d5a04SPhilipp Schuster     #[error("No control header descriptor")]
270249e864SSebastien Boeuf     NoControlHeaderDescriptor,
28f151a860SSebastien Boeuf     /// Missing the data descriptor in the chain.
29ea6d5a04SPhilipp Schuster     #[error("Missing the data descriptor in the chain")]
30f151a860SSebastien Boeuf     NoDataDescriptor,
3140dc3e7cSSebastien Boeuf     /// No status descriptor
32ea6d5a04SPhilipp Schuster     #[error("No status descriptor")]
3340dc3e7cSSebastien Boeuf     NoStatusDescriptor,
340249e864SSebastien Boeuf     /// Failed adding used index
35ea6d5a04SPhilipp Schuster     #[error("Failed adding used index")]
36ea6d5a04SPhilipp Schuster     QueueAddUsed(#[source] virtio_queue::Error),
370249e864SSebastien Boeuf     /// Failed creating an iterator over the queue
38ea6d5a04SPhilipp Schuster     #[error("Failed creating an iterator over the queue")]
39ea6d5a04SPhilipp Schuster     QueueIterator(#[source] virtio_queue::Error),
400249e864SSebastien Boeuf     /// Failed enabling notification for the queue
41ea6d5a04SPhilipp Schuster     #[error("Failed enabling notification for the queue")]
42ea6d5a04SPhilipp Schuster     QueueEnableNotification(#[source] virtio_queue::Error),
4340dc3e7cSSebastien Boeuf }
4440dc3e7cSSebastien Boeuf 
4540dc3e7cSSebastien Boeuf type Result<T> = std::result::Result<T, Error>;
4640dc3e7cSSebastien Boeuf 
4740dc3e7cSSebastien Boeuf #[repr(C, packed)]
4840dc3e7cSSebastien Boeuf #[derive(Debug, Clone, Copy, Default)]
4940dc3e7cSSebastien Boeuf pub struct ControlHeader {
5040dc3e7cSSebastien Boeuf     pub class: u8,
5140dc3e7cSSebastien Boeuf     pub cmd: u8,
5240dc3e7cSSebastien Boeuf }
5340dc3e7cSSebastien Boeuf 
548ee253cdSWei Liu // SAFETY: ControlHeader only contains a series of integers
5540dc3e7cSSebastien Boeuf unsafe impl ByteValued for ControlHeader {}
5640dc3e7cSSebastien Boeuf 
5740dc3e7cSSebastien Boeuf pub struct CtrlQueue {
5840dc3e7cSSebastien Boeuf     pub taps: Vec<Tap>,
5940dc3e7cSSebastien Boeuf }
6040dc3e7cSSebastien Boeuf 
6140dc3e7cSSebastien Boeuf impl CtrlQueue {
new(taps: Vec<Tap>) -> Self6240dc3e7cSSebastien Boeuf     pub fn new(taps: Vec<Tap>) -> Self {
6340dc3e7cSSebastien Boeuf         CtrlQueue { taps }
6440dc3e7cSSebastien Boeuf     }
6540dc3e7cSSebastien Boeuf 
process( &mut self, mem: &GuestMemoryMmap, queue: &mut Queue, access_platform: Option<&Arc<dyn AccessPlatform>>, ) -> Result<()>660249e864SSebastien Boeuf     pub fn process(
670249e864SSebastien Boeuf         &mut self,
68a423bf13SSebastien Boeuf         mem: &GuestMemoryMmap,
69a423bf13SSebastien Boeuf         queue: &mut Queue,
704becb11aSSebastien Boeuf         access_platform: Option<&Arc<dyn AccessPlatform>>,
71b9aeaf66SRob Bradford     ) -> Result<()> {
7287f57f7cSSebastien Boeuf         while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
730249e864SSebastien Boeuf             let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
740249e864SSebastien Boeuf 
750249e864SSebastien Boeuf             let ctrl_hdr: ControlHeader = desc_chain
760249e864SSebastien Boeuf                 .memory()
7777df4e67SSebastien Boeuf                 .read_obj(
7877df4e67SSebastien Boeuf                     ctrl_desc
7977df4e67SSebastien Boeuf                         .addr()
80059e787cSSebastien Boeuf                         .translate_gva(access_platform, ctrl_desc.len() as usize),
8177df4e67SSebastien Boeuf                 )
820249e864SSebastien Boeuf                 .map_err(Error::GuestMemory)?;
83f151a860SSebastien Boeuf             let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
844becb11aSSebastien Boeuf 
8577df4e67SSebastien Boeuf             let data_desc_addr = data_desc
8677df4e67SSebastien Boeuf                 .addr()
87059e787cSSebastien Boeuf                 .translate_gva(access_platform, data_desc.len() as usize);
884becb11aSSebastien Boeuf 
890249e864SSebastien Boeuf             let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
9040dc3e7cSSebastien Boeuf 
9140dc3e7cSSebastien Boeuf             let ok = match u32::from(ctrl_hdr.class) {
9240dc3e7cSSebastien Boeuf                 VIRTIO_NET_CTRL_MQ => {
930249e864SSebastien Boeuf                     let queue_pairs = desc_chain
940249e864SSebastien Boeuf                         .memory()
954becb11aSSebastien Boeuf                         .read_obj::<u16>(data_desc_addr)
9640dc3e7cSSebastien Boeuf                         .map_err(Error::GuestMemory)?;
9740dc3e7cSSebastien Boeuf                     if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
9840dc3e7cSSebastien Boeuf                         warn!("Unsupported command: {}", ctrl_hdr.cmd);
9940dc3e7cSSebastien Boeuf                         false
10040dc3e7cSSebastien Boeuf                     } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
10140dc3e7cSSebastien Boeuf                         || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
10240dc3e7cSSebastien Boeuf                     {
10340dc3e7cSSebastien Boeuf                         warn!("Number of MQ pairs out of range: {}", queue_pairs);
10440dc3e7cSSebastien Boeuf                         false
10540dc3e7cSSebastien Boeuf                     } else {
10640dc3e7cSSebastien Boeuf                         info!("Number of MQ pairs requested: {}", queue_pairs);
10740dc3e7cSSebastien Boeuf                         true
10840dc3e7cSSebastien Boeuf                     }
10940dc3e7cSSebastien Boeuf                 }
11040dc3e7cSSebastien Boeuf                 VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
1110249e864SSebastien Boeuf                     let features = desc_chain
1120249e864SSebastien Boeuf                         .memory()
1134becb11aSSebastien Boeuf                         .read_obj::<u64>(data_desc_addr)
11440dc3e7cSSebastien Boeuf                         .map_err(Error::GuestMemory)?;
11540dc3e7cSSebastien Boeuf                     if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
11640dc3e7cSSebastien Boeuf                         warn!("Unsupported command: {}", ctrl_hdr.cmd);
11740dc3e7cSSebastien Boeuf                         false
11840dc3e7cSSebastien Boeuf                     } else {
11940dc3e7cSSebastien Boeuf                         let mut ok = true;
12040dc3e7cSSebastien Boeuf                         for tap in self.taps.iter_mut() {
12140dc3e7cSSebastien Boeuf                             info!("Reprogramming tap offload with features: {}", features);
12240dc3e7cSSebastien Boeuf                             tap.set_offload(virtio_features_to_tap_offload(features))
12340dc3e7cSSebastien Boeuf                                 .map_err(|e| {
12440dc3e7cSSebastien Boeuf                                     error!("Error programming tap offload: {:?}", e);
12540dc3e7cSSebastien Boeuf                                     ok = false
12640dc3e7cSSebastien Boeuf                                 })
12740dc3e7cSSebastien Boeuf                                 .ok();
12840dc3e7cSSebastien Boeuf                         }
12940dc3e7cSSebastien Boeuf                         ok
13040dc3e7cSSebastien Boeuf                     }
13140dc3e7cSSebastien Boeuf                 }
13240dc3e7cSSebastien Boeuf                 _ => {
13340dc3e7cSSebastien Boeuf                     warn!("Unsupported command {:?}", ctrl_hdr);
13440dc3e7cSSebastien Boeuf                     false
13540dc3e7cSSebastien Boeuf                 }
13640dc3e7cSSebastien Boeuf             };
13740dc3e7cSSebastien Boeuf 
1380249e864SSebastien Boeuf             desc_chain
1390249e864SSebastien Boeuf                 .memory()
1400249e864SSebastien Boeuf                 .write_obj(
14140dc3e7cSSebastien Boeuf                     if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
14277df4e67SSebastien Boeuf                     status_desc
14377df4e67SSebastien Boeuf                         .addr()
144059e787cSSebastien Boeuf                         .translate_gva(access_platform, status_desc.len() as usize),
14540dc3e7cSSebastien Boeuf                 )
14640dc3e7cSSebastien Boeuf                 .map_err(Error::GuestMemory)?;
1470249e864SSebastien Boeuf             let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
148a4859ffeSSebastien Boeuf 
149a4859ffeSSebastien Boeuf             queue
150a4859ffeSSebastien Boeuf                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
151a4859ffeSSebastien Boeuf                 .map_err(Error::QueueAddUsed)?;
1521fc3fef6SRob Bradford 
1531fc3fef6SRob Bradford             if !queue
154a423bf13SSebastien Boeuf                 .enable_notification(mem)
1551fc3fef6SRob Bradford                 .map_err(Error::QueueEnableNotification)?
1561fc3fef6SRob Bradford             {
1571fc3fef6SRob Bradford                 break;
1581fc3fef6SRob Bradford             }
15940dc3e7cSSebastien Boeuf         }
16040dc3e7cSSebastien Boeuf 
161b9aeaf66SRob Bradford         Ok(())
16240dc3e7cSSebastien Boeuf     }
16340dc3e7cSSebastien Boeuf }
164