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