xref: /cloud-hypervisor/net_util/src/queue_pair.rs (revision 67896333e3c5c2f222953abdb4c2b5fd5c459fbe)
148faf3abSRob Bradford // Copyright (c) 2020 Intel Corporation. All rights reserved.
248faf3abSRob Bradford //
348faf3abSRob Bradford // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
448faf3abSRob Bradford 
517766fceSRob Bradford use std::io;
648faf3abSRob Bradford use std::num::Wrapping;
717766fceSRob Bradford use std::os::unix::io::{AsRawFd, RawFd};
817766fceSRob Bradford use std::sync::atomic::{AtomicU64, Ordering};
917766fceSRob Bradford use std::sync::Arc;
1088a9f799SRob Bradford 
1188a9f799SRob Bradford use rate_limiter::{RateLimiter, TokenType};
12f39b08f2SBo Chen use thiserror::Error;
13a423bf13SSebastien Boeuf use virtio_queue::{Queue, QueueOwnedT, QueueT};
14b29edfbeSRob Bradford use vm_memory::bitmap::Bitmap;
15a423bf13SSebastien Boeuf use vm_memory::{Bytes, GuestMemory};
1677df4e67SSebastien Boeuf use vm_virtio::{AccessPlatform, Translatable};
1748faf3abSRob Bradford 
1888a9f799SRob Bradford use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
1988a9f799SRob Bradford 
2048faf3abSRob Bradford #[derive(Clone)]
2148faf3abSRob Bradford pub struct TxVirtio {
2248faf3abSRob Bradford     pub counter_bytes: Wrapping<u64>,
2348faf3abSRob Bradford     pub counter_frames: Wrapping<u64>,
2433200157Sihciah     iovecs: IovecBuffer,
2548faf3abSRob Bradford }
2648faf3abSRob Bradford 
2748faf3abSRob Bradford impl Default for TxVirtio {
default() -> Self2848faf3abSRob Bradford     fn default() -> Self {
2948faf3abSRob Bradford         Self::new()
3048faf3abSRob Bradford     }
3148faf3abSRob Bradford }
3248faf3abSRob Bradford 
3348faf3abSRob Bradford impl TxVirtio {
new() -> Self3448faf3abSRob Bradford     pub fn new() -> Self {
3548faf3abSRob Bradford         TxVirtio {
3648faf3abSRob Bradford             counter_bytes: Wrapping(0),
3748faf3abSRob Bradford             counter_frames: Wrapping(0),
3833200157Sihciah             iovecs: IovecBuffer::new(),
3948faf3abSRob Bradford         }
4048faf3abSRob Bradford     }
4148faf3abSRob Bradford 
process_desc_chain<B: Bitmap + 'static>( &mut self, mem: &vm_memory::GuestMemoryMmap<B>, tap: &Tap, queue: &mut Queue, rate_limiter: &mut Option<RateLimiter>, access_platform: Option<&Arc<dyn AccessPlatform>>, ) -> Result<bool, NetQueuePairError>42b29edfbeSRob Bradford     pub fn process_desc_chain<B: Bitmap + 'static>(
434ed0e1a3SSebastien Boeuf         &mut self,
44b29edfbeSRob Bradford         mem: &vm_memory::GuestMemoryMmap<B>,
455636d915SRob Bradford         tap: &Tap,
46a423bf13SSebastien Boeuf         queue: &mut Queue,
47b176ddfeSBo Chen         rate_limiter: &mut Option<RateLimiter>,
484becb11aSSebastien Boeuf         access_platform: Option<&Arc<dyn AccessPlatform>>,
49b45264afSRob Bradford     ) -> Result<bool, NetQueuePairError> {
50b45264afSRob Bradford         let mut retry_write = false;
5197b3c9b7SBo Chen         let mut rate_limit_reached = false;
520249e864SSebastien Boeuf 
5387f57f7cSSebastien Boeuf         while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
5497b3c9b7SBo Chen             if rate_limit_reached {
5587f57f7cSSebastien Boeuf                 queue.go_to_previous_position();
5697b3c9b7SBo Chen                 break;
5797b3c9b7SBo Chen             }
5897b3c9b7SBo Chen 
590249e864SSebastien Boeuf             let mut next_desc = desc_chain.next();
6048faf3abSRob Bradford 
6133200157Sihciah             let mut iovecs = self.iovecs.borrow();
6248faf3abSRob Bradford             while let Some(desc) = next_desc {
63059e787cSSebastien Boeuf                 let desc_addr = desc
64059e787cSSebastien Boeuf                     .addr()
65059e787cSSebastien Boeuf                     .translate_gva(access_platform, desc.len() as usize);
660249e864SSebastien Boeuf                 if !desc.is_write_only() && desc.len() > 0 {
670249e864SSebastien Boeuf                     let buf = desc_chain
680249e864SSebastien Boeuf                         .memory()
694becb11aSSebastien Boeuf                         .get_slice(desc_addr, desc.len() as usize)
704ed0e1a3SSebastien Boeuf                         .map_err(NetQueuePairError::GuestMemory)?
7107d1208dSRob Bradford                         .ptr_guard_mut();
724ed0e1a3SSebastien Boeuf                     let iovec = libc::iovec {
7307d1208dSRob Bradford                         iov_base: buf.as_ptr() as *mut libc::c_void,
740249e864SSebastien Boeuf                         iov_len: desc.len() as libc::size_t,
754ed0e1a3SSebastien Boeuf                     };
764ed0e1a3SSebastien Boeuf                     iovecs.push(iovec);
77c7d1cfbdSRob Bradford                 } else {
78c7d1cfbdSRob Bradford                     error!(
79c7d1cfbdSRob Bradford                         "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
804becb11aSSebastien Boeuf                         desc_addr.0,
81c7d1cfbdSRob Bradford                         desc.len(),
82c7d1cfbdSRob Bradford                         desc.is_write_only()
83c7d1cfbdSRob Bradford                     );
84c7d1cfbdSRob Bradford                     return Err(NetQueuePairError::DescriptorChainInvalid);
8548faf3abSRob Bradford                 }
860249e864SSebastien Boeuf                 next_desc = desc_chain.next();
8748faf3abSRob Bradford             }
8848faf3abSRob Bradford 
8997b3c9b7SBo Chen             let len = if !iovecs.is_empty() {
908a7f4b47SWei Liu                 // SAFETY: FFI call with correct arguments
914ed0e1a3SSebastien Boeuf                 let result = unsafe {
924ed0e1a3SSebastien Boeuf                     libc::writev(
934ed0e1a3SSebastien Boeuf                         tap.as_raw_fd() as libc::c_int,
94aac614e2SYu Li                         iovecs.as_ptr(),
954ed0e1a3SSebastien Boeuf                         iovecs.len() as libc::c_int,
964ed0e1a3SSebastien Boeuf                     )
9748faf3abSRob Bradford                 };
9843f1a328SRob Bradford 
994ed0e1a3SSebastien Boeuf                 if result < 0 {
1004ed0e1a3SSebastien Boeuf                     let e = std::io::Error::last_os_error();
10157842858SRob Bradford 
10257842858SRob Bradford                     /* EAGAIN */
10357842858SRob Bradford                     if e.kind() == std::io::ErrorKind::WouldBlock {
10487f57f7cSSebastien Boeuf                         queue.go_to_previous_position();
105b45264afSRob Bradford                         retry_write = true;
10657842858SRob Bradford                         break;
10757842858SRob Bradford                     }
10857842858SRob Bradford                     error!("net: tx: failed writing to tap: {}", e);
1094ed0e1a3SSebastien Boeuf                     return Err(NetQueuePairError::WriteTap(e));
1104ed0e1a3SSebastien Boeuf                 }
11148faf3abSRob Bradford 
112559faa27SBo Chen                 if (result as usize) < vnet_hdr_len() {
113559faa27SBo Chen                     return Err(NetQueuePairError::InvalidVirtioNetHeader);
114559faa27SBo Chen                 }
115559faa27SBo Chen 
1164ed0e1a3SSebastien Boeuf                 self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
11748faf3abSRob Bradford                 self.counter_frames += Wrapping(1);
11897b3c9b7SBo Chen 
11997b3c9b7SBo Chen                 result as u32
12097b3c9b7SBo Chen             } else {
12197b3c9b7SBo Chen                 0
12297b3c9b7SBo Chen             };
12348faf3abSRob Bradford 
12497b3c9b7SBo Chen             // For the sake of simplicity (similar to the RX rate limiting), we always
12597b3c9b7SBo Chen             // let the 'last' descriptor chain go-through even if it was over the rate
12697b3c9b7SBo Chen             // limit, and simply stop processing oncoming `avail_desc` if any.
12797b3c9b7SBo Chen             if let Some(rate_limiter) = rate_limiter {
12897b3c9b7SBo Chen                 rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
12997b3c9b7SBo Chen                     || !rate_limiter.consume(len as u64, TokenType::Bytes);
13097b3c9b7SBo Chen             }
1310249e864SSebastien Boeuf 
1320249e864SSebastien Boeuf             queue
133a4859ffeSSebastien Boeuf                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
1340249e864SSebastien Boeuf                 .map_err(NetQueuePairError::QueueAddUsed)?;
13587f57f7cSSebastien Boeuf 
1361fc3fef6SRob Bradford             if !queue
137a423bf13SSebastien Boeuf                 .enable_notification(mem)
1381fc3fef6SRob Bradford                 .map_err(NetQueuePairError::QueueEnableNotification)?
1391fc3fef6SRob Bradford             {
1401fc3fef6SRob Bradford                 break;
1411fc3fef6SRob Bradford             }
14248faf3abSRob Bradford         }
1434ed0e1a3SSebastien Boeuf 
144b45264afSRob Bradford         Ok(retry_write)
14548faf3abSRob Bradford     }
14648faf3abSRob Bradford }
14748faf3abSRob Bradford 
14848faf3abSRob Bradford #[derive(Clone)]
14948faf3abSRob Bradford pub struct RxVirtio {
15048faf3abSRob Bradford     pub counter_bytes: Wrapping<u64>,
15148faf3abSRob Bradford     pub counter_frames: Wrapping<u64>,
15233200157Sihciah     iovecs: IovecBuffer,
15348faf3abSRob Bradford }
15448faf3abSRob Bradford 
15548faf3abSRob Bradford impl Default for RxVirtio {
default() -> Self15648faf3abSRob Bradford     fn default() -> Self {
15748faf3abSRob Bradford         Self::new()
15848faf3abSRob Bradford     }
15948faf3abSRob Bradford }
16048faf3abSRob Bradford 
16148faf3abSRob Bradford impl RxVirtio {
new() -> Self16248faf3abSRob Bradford     pub fn new() -> Self {
16348faf3abSRob Bradford         RxVirtio {
16448faf3abSRob Bradford             counter_bytes: Wrapping(0),
16548faf3abSRob Bradford             counter_frames: Wrapping(0),
16633200157Sihciah             iovecs: IovecBuffer::new(),
16748faf3abSRob Bradford         }
16848faf3abSRob Bradford     }
16948faf3abSRob Bradford 
process_desc_chain<B: Bitmap + 'static>( &mut self, mem: &vm_memory::GuestMemoryMmap<B>, tap: &Tap, queue: &mut Queue, rate_limiter: &mut Option<RateLimiter>, access_platform: Option<&Arc<dyn AccessPlatform>>, ) -> Result<bool, NetQueuePairError>170b29edfbeSRob Bradford     pub fn process_desc_chain<B: Bitmap + 'static>(
17148faf3abSRob Bradford         &mut self,
172b29edfbeSRob Bradford         mem: &vm_memory::GuestMemoryMmap<B>,
1735636d915SRob Bradford         tap: &Tap,
174a423bf13SSebastien Boeuf         queue: &mut Queue,
17532ad4982SBo Chen         rate_limiter: &mut Option<RateLimiter>,
1764becb11aSSebastien Boeuf         access_platform: Option<&Arc<dyn AccessPlatform>>,
1774ed0e1a3SSebastien Boeuf     ) -> Result<bool, NetQueuePairError> {
1784ed0e1a3SSebastien Boeuf         let mut exhausted_descs = true;
17932ad4982SBo Chen         let mut rate_limit_reached = false;
18032ad4982SBo Chen 
18187f57f7cSSebastien Boeuf         while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
18232ad4982SBo Chen             if rate_limit_reached {
18332ad4982SBo Chen                 exhausted_descs = false;
18487f57f7cSSebastien Boeuf                 queue.go_to_previous_position();
18532ad4982SBo Chen                 break;
18632ad4982SBo Chen             }
18732ad4982SBo Chen 
1880249e864SSebastien Boeuf             let desc = desc_chain
1890249e864SSebastien Boeuf                 .next()
1900249e864SSebastien Boeuf                 .ok_or(NetQueuePairError::DescriptorChainTooShort)?;
1914becb11aSSebastien Boeuf 
19277df4e67SSebastien Boeuf             let num_buffers_addr = desc_chain
19377df4e67SSebastien Boeuf                 .memory()
19477df4e67SSebastien Boeuf                 .checked_offset(
195059e787cSSebastien Boeuf                     desc.addr()
196059e787cSSebastien Boeuf                         .translate_gva(access_platform, desc.len() as usize),
19777df4e67SSebastien Boeuf                     10,
1984becb11aSSebastien Boeuf                 )
1994d9a2b17SBo Chen                 .ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
2000249e864SSebastien Boeuf             let mut next_desc = Some(desc);
20148faf3abSRob Bradford 
20233200157Sihciah             let mut iovecs = self.iovecs.borrow();
2034ed0e1a3SSebastien Boeuf             while let Some(desc) = next_desc {
204059e787cSSebastien Boeuf                 let desc_addr = desc
205059e787cSSebastien Boeuf                     .addr()
206059e787cSSebastien Boeuf                     .translate_gva(access_platform, desc.len() as usize);
2070249e864SSebastien Boeuf                 if desc.is_write_only() && desc.len() > 0 {
2080249e864SSebastien Boeuf                     let buf = desc_chain
2090249e864SSebastien Boeuf                         .memory()
2104becb11aSSebastien Boeuf                         .get_slice(desc_addr, desc.len() as usize)
2114ed0e1a3SSebastien Boeuf                         .map_err(NetQueuePairError::GuestMemory)?
21207d1208dSRob Bradford                         .ptr_guard_mut();
2134ed0e1a3SSebastien Boeuf                     let iovec = libc::iovec {
21407d1208dSRob Bradford                         iov_base: buf.as_ptr() as *mut libc::c_void,
2150249e864SSebastien Boeuf                         iov_len: desc.len() as libc::size_t,
21648faf3abSRob Bradford                     };
2174ed0e1a3SSebastien Boeuf                     iovecs.push(iovec);
218c7d1cfbdSRob Bradford                 } else {
219c7d1cfbdSRob Bradford                     error!(
220c7d1cfbdSRob Bradford                         "Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
2214becb11aSSebastien Boeuf                         desc_addr.0,
222c7d1cfbdSRob Bradford                         desc.len(),
223c7d1cfbdSRob Bradford                         desc.is_write_only()
224c7d1cfbdSRob Bradford                     );
225c7d1cfbdSRob Bradford                     return Err(NetQueuePairError::DescriptorChainInvalid);
22648faf3abSRob Bradford                 }
2270249e864SSebastien Boeuf                 next_desc = desc_chain.next();
22848faf3abSRob Bradford             }
2294ed0e1a3SSebastien Boeuf 
2304ed0e1a3SSebastien Boeuf             let len = if !iovecs.is_empty() {
2318a7f4b47SWei Liu                 // SAFETY: FFI call with correct arguments
2324ed0e1a3SSebastien Boeuf                 let result = unsafe {
2334ed0e1a3SSebastien Boeuf                     libc::readv(
2344ed0e1a3SSebastien Boeuf                         tap.as_raw_fd() as libc::c_int,
235aac614e2SYu Li                         iovecs.as_ptr(),
2364ed0e1a3SSebastien Boeuf                         iovecs.len() as libc::c_int,
2374ed0e1a3SSebastien Boeuf                     )
2384ed0e1a3SSebastien Boeuf                 };
2394ed0e1a3SSebastien Boeuf                 if result < 0 {
2404ed0e1a3SSebastien Boeuf                     let e = std::io::Error::last_os_error();
2414ed0e1a3SSebastien Boeuf                     exhausted_descs = false;
24287f57f7cSSebastien Boeuf                     queue.go_to_previous_position();
2434ed0e1a3SSebastien Boeuf 
24457842858SRob Bradford                     /* EAGAIN */
24557842858SRob Bradford                     if e.kind() == std::io::ErrorKind::WouldBlock {
24648faf3abSRob Bradford                         break;
24748faf3abSRob Bradford                     }
2484ed0e1a3SSebastien Boeuf 
2494ed0e1a3SSebastien Boeuf                     error!("net: rx: failed reading from tap: {}", e);
2504ed0e1a3SSebastien Boeuf                     return Err(NetQueuePairError::ReadTap(e));
25148faf3abSRob Bradford                 }
25248faf3abSRob Bradford 
253559faa27SBo Chen                 if (result as usize) < vnet_hdr_len() {
254559faa27SBo Chen                     return Err(NetQueuePairError::InvalidVirtioNetHeader);
255559faa27SBo Chen                 }
256559faa27SBo Chen 
2574ed0e1a3SSebastien Boeuf                 // Write num_buffers to guest memory. We simply write 1 as we
2584ed0e1a3SSebastien Boeuf                 // never spread the frame over more than one descriptor chain.
2590249e864SSebastien Boeuf                 desc_chain
2600249e864SSebastien Boeuf                     .memory()
2610249e864SSebastien Boeuf                     .write_obj(1u16, num_buffers_addr)
2624ed0e1a3SSebastien Boeuf                     .map_err(NetQueuePairError::GuestMemory)?;
2634ed0e1a3SSebastien Boeuf 
2644ed0e1a3SSebastien Boeuf                 self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
26548faf3abSRob Bradford                 self.counter_frames += Wrapping(1);
26648faf3abSRob Bradford 
2674ed0e1a3SSebastien Boeuf                 result as u32
26848faf3abSRob Bradford             } else {
2694ed0e1a3SSebastien Boeuf                 0
2704ed0e1a3SSebastien Boeuf             };
2714ed0e1a3SSebastien Boeuf 
27232ad4982SBo Chen             // For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
27332ad4982SBo Chen             // RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
27432ad4982SBo Chen             // chain go-through even if it was over the rate limit, and simply stop
27532ad4982SBo Chen             // processing oncoming `avail_desc` if any.
27632ad4982SBo Chen             if let Some(rate_limiter) = rate_limiter {
27732ad4982SBo Chen                 rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
27832ad4982SBo Chen                     || !rate_limiter.consume(len as u64, TokenType::Bytes);
27932ad4982SBo Chen             }
2800249e864SSebastien Boeuf 
2810249e864SSebastien Boeuf             queue
282a4859ffeSSebastien Boeuf                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
2830249e864SSebastien Boeuf                 .map_err(NetQueuePairError::QueueAddUsed)?;
28487f57f7cSSebastien Boeuf 
2851fc3fef6SRob Bradford             if !queue
286a423bf13SSebastien Boeuf                 .enable_notification(mem)
2871fc3fef6SRob Bradford                 .map_err(NetQueuePairError::QueueEnableNotification)?
2881fc3fef6SRob Bradford             {
2891fc3fef6SRob Bradford                 break;
2901fc3fef6SRob Bradford             }
29148faf3abSRob Bradford         }
2924ed0e1a3SSebastien Boeuf 
2934ed0e1a3SSebastien Boeuf         Ok(exhausted_descs)
29448faf3abSRob Bradford     }
29548faf3abSRob Bradford }
29617766fceSRob Bradford 
29717766fceSRob Bradford #[derive(Default, Clone)]
29833200157Sihciah struct IovecBuffer(Vec<libc::iovec>);
29933200157Sihciah 
30033200157Sihciah // SAFETY: Implementing Send for IovecBuffer is safe as the pointer inside is iovec.
30133200157Sihciah // The iovecs are usually constructed from virtio descriptors, which are safe to send across
30233200157Sihciah // threads.
30333200157Sihciah unsafe impl Send for IovecBuffer {}
30433200157Sihciah // SAFETY: Implementing Sync for IovecBuffer is safe as the pointer inside is iovec.
30533200157Sihciah // The iovecs are usually constructed from virtio descriptors, which are safe to access from
30633200157Sihciah // multiple threads.
30733200157Sihciah unsafe impl Sync for IovecBuffer {}
30833200157Sihciah 
30933200157Sihciah impl IovecBuffer {
new() -> Self31033200157Sihciah     fn new() -> Self {
31133200157Sihciah         // Here we use 4 as the default capacity because it is enough for most cases.
31233200157Sihciah         const DEFAULT_CAPACITY: usize = 4;
31333200157Sihciah         IovecBuffer(Vec::with_capacity(DEFAULT_CAPACITY))
31433200157Sihciah     }
31533200157Sihciah 
borrow(&mut self) -> IovecBufferBorrowed<'_>31633200157Sihciah     fn borrow(&mut self) -> IovecBufferBorrowed<'_> {
31733200157Sihciah         IovecBufferBorrowed(&mut self.0)
31833200157Sihciah     }
31933200157Sihciah }
32033200157Sihciah 
32133200157Sihciah struct IovecBufferBorrowed<'a>(&'a mut Vec<libc::iovec>);
32233200157Sihciah 
3230aab960bSRuoqing He impl std::ops::Deref for IovecBufferBorrowed<'_> {
32433200157Sihciah     type Target = Vec<libc::iovec>;
32533200157Sihciah 
deref(&self) -> &Self::Target32633200157Sihciah     fn deref(&self) -> &Self::Target {
32733200157Sihciah         self.0
32833200157Sihciah     }
32933200157Sihciah }
33033200157Sihciah 
3310aab960bSRuoqing He impl std::ops::DerefMut for IovecBufferBorrowed<'_> {
deref_mut(&mut self) -> &mut Self::Target33233200157Sihciah     fn deref_mut(&mut self) -> &mut Self::Target {
33333200157Sihciah         self.0
33433200157Sihciah     }
33533200157Sihciah }
33633200157Sihciah 
33733200157Sihciah impl Drop for IovecBufferBorrowed<'_> {
drop(&mut self)33833200157Sihciah     fn drop(&mut self) {
33933200157Sihciah         // Clear the buffer to make sure old values are not used after
34033200157Sihciah         self.0.clear();
34133200157Sihciah     }
34233200157Sihciah }
34333200157Sihciah 
34433200157Sihciah #[derive(Default, Clone)]
34517766fceSRob Bradford pub struct NetCounters {
34617766fceSRob Bradford     pub tx_bytes: Arc<AtomicU64>,
34717766fceSRob Bradford     pub tx_frames: Arc<AtomicU64>,
34817766fceSRob Bradford     pub rx_bytes: Arc<AtomicU64>,
34917766fceSRob Bradford     pub rx_frames: Arc<AtomicU64>,
35017766fceSRob Bradford }
35117766fceSRob Bradford 
352f39b08f2SBo Chen #[derive(Error, Debug)]
35317766fceSRob Bradford pub enum NetQueuePairError {
3542af2cc53SBo Chen     #[error("No memory configured")]
35517766fceSRob Bradford     NoMemoryConfigured,
356*67896333SPhilipp Schuster     #[error("Error registering listener")]
357fd5cfb75SPhilipp Schuster     RegisterListener(#[source] io::Error),
358*67896333SPhilipp Schuster     #[error("Error unregistering listener")]
359fd5cfb75SPhilipp Schuster     UnregisterListener(#[source] io::Error),
360*67896333SPhilipp Schuster     #[error("Error writing to the TAP device")]
361fd5cfb75SPhilipp Schuster     WriteTap(#[source] io::Error),
362*67896333SPhilipp Schuster     #[error("Error reading from the TAP device")]
363fd5cfb75SPhilipp Schuster     ReadTap(#[source] io::Error),
364*67896333SPhilipp Schuster     #[error("Error related to guest memory")]
365fd5cfb75SPhilipp Schuster     GuestMemory(#[source] vm_memory::GuestMemoryError),
366*67896333SPhilipp Schuster     #[error("Returned an error while iterating through the queue")]
367fd5cfb75SPhilipp Schuster     QueueIteratorFailed(#[source] virtio_queue::Error),
3682af2cc53SBo Chen     #[error("Descriptor chain is too short")]
3690249e864SSebastien Boeuf     DescriptorChainTooShort,
3702af2cc53SBo Chen     #[error("Descriptor chain does not contain valid descriptors")]
371c7d1cfbdSRob Bradford     DescriptorChainInvalid,
372*67896333SPhilipp Schuster     #[error("Failed to determine if queue needed notification")]
373fd5cfb75SPhilipp Schuster     QueueNeedsNotification(#[source] virtio_queue::Error),
374*67896333SPhilipp Schuster     #[error("Failed to enable notification on the queue")]
375fd5cfb75SPhilipp Schuster     QueueEnableNotification(#[source] virtio_queue::Error),
376*67896333SPhilipp Schuster     #[error("Failed to add used index to the queue")]
377fd5cfb75SPhilipp Schuster     QueueAddUsed(#[source] virtio_queue::Error),
3784d9a2b17SBo Chen     #[error("Descriptor with invalid virtio-net header")]
3794d9a2b17SBo Chen     DescriptorInvalidHeader,
380559faa27SBo Chen     #[error("Invalid virtio-net header")]
381559faa27SBo Chen     InvalidVirtioNetHeader,
38217766fceSRob Bradford }
38317766fceSRob Bradford 
38417766fceSRob Bradford pub struct NetQueuePair {
38517766fceSRob Bradford     pub tap: Tap,
386b45264afSRob Bradford     // With epoll each FD must be unique. So in order to filter the
387b45264afSRob Bradford     // events we need to get a second FD responding to the original
388b45264afSRob Bradford     // device so that we can send EPOLLOUT and EPOLLIN to separate
389b45264afSRob Bradford     // events.
390b45264afSRob Bradford     pub tap_for_write_epoll: Tap,
39117766fceSRob Bradford     pub rx: RxVirtio,
39217766fceSRob Bradford     pub tx: TxVirtio,
39317766fceSRob Bradford     pub epoll_fd: Option<RawFd>,
39417766fceSRob Bradford     pub rx_tap_listening: bool,
395b45264afSRob Bradford     pub tx_tap_listening: bool,
39617766fceSRob Bradford     pub counters: NetCounters,
397d9680c4cSRob Bradford     pub tap_rx_event_id: u16,
398b45264afSRob Bradford     pub tap_tx_event_id: u16,
39932ad4982SBo Chen     pub rx_desc_avail: bool,
40032ad4982SBo Chen     pub rx_rate_limiter: Option<RateLimiter>,
401b176ddfeSBo Chen     pub tx_rate_limiter: Option<RateLimiter>,
4024becb11aSSebastien Boeuf     pub access_platform: Option<Arc<dyn AccessPlatform>>,
40317766fceSRob Bradford }
40417766fceSRob Bradford 
40517766fceSRob Bradford impl NetQueuePair {
process_tx<B: Bitmap + 'static>( &mut self, mem: &vm_memory::GuestMemoryMmap<B>, queue: &mut Queue, ) -> Result<bool, NetQueuePairError>406b29edfbeSRob Bradford     pub fn process_tx<B: Bitmap + 'static>(
4070249e864SSebastien Boeuf         &mut self,
408b29edfbeSRob Bradford         mem: &vm_memory::GuestMemoryMmap<B>,
409a423bf13SSebastien Boeuf         queue: &mut Queue,
4100249e864SSebastien Boeuf     ) -> Result<bool, NetQueuePairError> {
4114becb11aSSebastien Boeuf         let tx_tap_retry = self.tx.process_desc_chain(
412a423bf13SSebastien Boeuf             mem,
4135636d915SRob Bradford             &self.tap,
4144becb11aSSebastien Boeuf             queue,
4154becb11aSSebastien Boeuf             &mut self.tx_rate_limiter,
4164becb11aSSebastien Boeuf             self.access_platform.as_ref(),
4174becb11aSSebastien Boeuf         )?;
418b45264afSRob Bradford 
419b45264afSRob Bradford         // We got told to try again when writing to the tap. Wait for the TAP to be writable
420b45264afSRob Bradford         if tx_tap_retry && !self.tx_tap_listening {
421b45264afSRob Bradford             register_listener(
422b45264afSRob Bradford                 self.epoll_fd.unwrap(),
423b45264afSRob Bradford                 self.tap_for_write_epoll.as_raw_fd(),
424b45264afSRob Bradford                 epoll::Events::EPOLLOUT,
425b45264afSRob Bradford                 u64::from(self.tap_tx_event_id),
426b45264afSRob Bradford             )
427b45264afSRob Bradford             .map_err(NetQueuePairError::RegisterListener)?;
428b45264afSRob Bradford             self.tx_tap_listening = true;
429b45264afSRob Bradford             info!("Writing to TAP returned EAGAIN. Listening for TAP to become writable.");
430b45264afSRob Bradford         } else if !tx_tap_retry && self.tx_tap_listening {
431b45264afSRob Bradford             unregister_listener(
432b45264afSRob Bradford                 self.epoll_fd.unwrap(),
433b45264afSRob Bradford                 self.tap_for_write_epoll.as_raw_fd(),
434b45264afSRob Bradford                 epoll::Events::EPOLLOUT,
435b45264afSRob Bradford                 u64::from(self.tap_tx_event_id),
436b45264afSRob Bradford             )
437b45264afSRob Bradford             .map_err(NetQueuePairError::UnregisterListener)?;
438b45264afSRob Bradford             self.tx_tap_listening = false;
439b45264afSRob Bradford             info!("Writing to TAP succeeded. No longer listening for TAP to become writable.");
440b45264afSRob Bradford         }
44117766fceSRob Bradford 
44217766fceSRob Bradford         self.counters
44317766fceSRob Bradford             .tx_bytes
44417766fceSRob Bradford             .fetch_add(self.tx.counter_bytes.0, Ordering::AcqRel);
44517766fceSRob Bradford         self.counters
44617766fceSRob Bradford             .tx_frames
44717766fceSRob Bradford             .fetch_add(self.tx.counter_frames.0, Ordering::AcqRel);
44817766fceSRob Bradford         self.tx.counter_bytes = Wrapping(0);
44917766fceSRob Bradford         self.tx.counter_frames = Wrapping(0);
45017766fceSRob Bradford 
4510249e864SSebastien Boeuf         queue
452a423bf13SSebastien Boeuf             .needs_notification(mem)
4530249e864SSebastien Boeuf             .map_err(NetQueuePairError::QueueNeedsNotification)
45417766fceSRob Bradford     }
45517766fceSRob Bradford 
process_rx<B: Bitmap + 'static>( &mut self, mem: &vm_memory::GuestMemoryMmap<B>, queue: &mut Queue, ) -> Result<bool, NetQueuePairError>456b29edfbeSRob Bradford     pub fn process_rx<B: Bitmap + 'static>(
4570249e864SSebastien Boeuf         &mut self,
458b29edfbeSRob Bradford         mem: &vm_memory::GuestMemoryMmap<B>,
459a423bf13SSebastien Boeuf         queue: &mut Queue,
4600249e864SSebastien Boeuf     ) -> Result<bool, NetQueuePairError> {
4614becb11aSSebastien Boeuf         self.rx_desc_avail = !self.rx.process_desc_chain(
462a423bf13SSebastien Boeuf             mem,
4635636d915SRob Bradford             &self.tap,
4644becb11aSSebastien Boeuf             queue,
4654becb11aSSebastien Boeuf             &mut self.rx_rate_limiter,
4664becb11aSSebastien Boeuf             self.access_platform.as_ref(),
4674becb11aSSebastien Boeuf         )?;
46832ad4982SBo Chen         let rate_limit_reached = self
46932ad4982SBo Chen             .rx_rate_limiter
47032ad4982SBo Chen             .as_ref()
471ab7b2946SRuoqing He             .is_some_and(|r| r.is_blocked());
47232ad4982SBo Chen 
47332ad4982SBo Chen         // Stop listening on the `RX_TAP_EVENT` when:
47442e9632cSJosh Soref         // 1) there is no available describes, or
47532ad4982SBo Chen         // 2) the RX rate limit is reached.
47632ad4982SBo Chen         if self.rx_tap_listening && (!self.rx_desc_avail || rate_limit_reached) {
4774ed0e1a3SSebastien Boeuf             unregister_listener(
4784ed0e1a3SSebastien Boeuf                 self.epoll_fd.unwrap(),
4794ed0e1a3SSebastien Boeuf                 self.tap.as_raw_fd(),
4804ed0e1a3SSebastien Boeuf                 epoll::Events::EPOLLIN,
481d9680c4cSRob Bradford                 u64::from(self.tap_rx_event_id),
4824ed0e1a3SSebastien Boeuf             )
4834ed0e1a3SSebastien Boeuf             .map_err(NetQueuePairError::UnregisterListener)?;
4844ed0e1a3SSebastien Boeuf             self.rx_tap_listening = false;
48517766fceSRob Bradford         }
48617766fceSRob Bradford 
4874ed0e1a3SSebastien Boeuf         self.counters
4884ed0e1a3SSebastien Boeuf             .rx_bytes
4894ed0e1a3SSebastien Boeuf             .fetch_add(self.rx.counter_bytes.0, Ordering::AcqRel);
4904ed0e1a3SSebastien Boeuf         self.counters
4914ed0e1a3SSebastien Boeuf             .rx_frames
4924ed0e1a3SSebastien Boeuf             .fetch_add(self.rx.counter_frames.0, Ordering::AcqRel);
4934ed0e1a3SSebastien Boeuf         self.rx.counter_bytes = Wrapping(0);
4944ed0e1a3SSebastien Boeuf         self.rx.counter_frames = Wrapping(0);
4954ed0e1a3SSebastien Boeuf 
4960249e864SSebastien Boeuf         queue
497a423bf13SSebastien Boeuf             .needs_notification(mem)
4980249e864SSebastien Boeuf             .map_err(NetQueuePairError::QueueNeedsNotification)
49917766fceSRob Bradford     }
50017766fceSRob Bradford }
501