diff --git a/Cargo.lock b/Cargo.lock index 2fdc7ee8b3..3eba7d54b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,14 +129,27 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "async-channel" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +dependencies = [ + "concurrent-queue", + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-lock" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener", - "event-listener-strategy", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -412,13 +425,33 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener", + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.0", "pin-project-lite", ] @@ -473,6 +506,12 @@ dependencies = [ "x86_64 0.15.1", ] +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + [[package]] name = "generic_once_cell" version = "0.1.1" @@ -561,6 +600,7 @@ dependencies = [ "anstyle", "anyhow", "arm-gic", + "async-channel", "async-lock", "async-trait", "bit_field", diff --git a/Cargo.toml b/Cargo.toml index a895abea8e..e8d797b6bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,6 +104,7 @@ async-lock = { version = "3.3.0", default-features = false } simple-shell = { version = "0.0.1", optional = true } volatile = { version = "0.5.4", features = ["unstable"] } anstyle = { version = "1", default-features = false } +async-channel = { version = "2.2", default-features = false } [dependencies.smoltcp] version = "0.11" diff --git a/src/drivers/net/virtio_mmio.rs b/src/drivers/net/virtio_mmio.rs index 331bf85eb1..140843026e 100644 --- a/src/drivers/net/virtio_mmio.rs +++ b/src/drivers/net/virtio_mmio.rs @@ -2,10 +2,8 @@ //! //! The module contains ... -use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec::Vec; -use core::cell::RefCell; use core::ptr; use core::ptr::read_volatile; use core::str::FromStr; @@ -138,17 +136,8 @@ impl VirtioNetDriver { isr_stat, notif_cfg, ctrl_vq: CtrlQueue::new(None), - recv_vqs: RxQueues::new( - Vec::>::new(), - Rc::new(RefCell::new(VecDeque::new())), - false, - ), - send_vqs: TxQueues::new( - Vec::>::new(), - Rc::new(RefCell::new(VecDeque::new())), - Vec::new(), - false, - ), + recv_vqs: RxQueues::new(Vec::>::new(), false), + send_vqs: TxQueues::new(Vec::>::new(), Vec::new(), false), num_vqs: 0, irq, mtu, diff --git a/src/drivers/net/virtio_net.rs b/src/drivers/net/virtio_net.rs index 31c74c73ca..a40a74a78f 100644 --- a/src/drivers/net/virtio_net.rs +++ b/src/drivers/net/virtio_net.rs @@ -2,10 +2,9 @@ //! //! The module contains ... -use alloc::collections::VecDeque; +use alloc::boxed::Box; use alloc::rc::Rc; use alloc::vec::Vec; -use core::cell::RefCell; use core::cmp::Ordering; use core::mem; @@ -31,9 +30,7 @@ use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg}; use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg}; use crate::drivers::virtio::virtqueue::packed::PackedVq; use crate::drivers::virtio::virtqueue::split::SplitVq; -use crate::drivers::virtio::virtqueue::{ - BuffSpec, BufferToken, Bytes, Transfer, Virtq, VqIndex, VqSize, -}; +use crate::drivers::virtio::virtqueue::{BuffSpec, BufferToken, Bytes, Virtq, VqIndex, VqSize}; use crate::executor::device::{RxToken, TxToken}; /// A wrapper struct for the raw configuration structure. @@ -155,19 +152,18 @@ enum MqCmd { pub struct RxQueues { vqs: Vec>, - poll_queue: Rc>>, + poll_sender: async_channel::Sender>, + poll_receiver: async_channel::Receiver>, is_multi: bool, } impl RxQueues { - pub fn new( - vqs: Vec>, - poll_queue: Rc>>, - is_multi: bool, - ) -> Self { + pub fn new(vqs: Vec>, is_multi: bool) -> Self { + let (poll_sender, poll_receiver) = async_channel::unbounded(); Self { vqs, - poll_queue, + poll_sender, + poll_receiver, is_multi, } } @@ -176,14 +172,8 @@ impl RxQueues { /// This currently include nothing. But in the future it might include among others:: /// * Calculating missing checksums /// * Merging receive buffers, by simply checking the poll_queue (if VIRTIO_NET_F_MRG_BUF) - fn post_processing(transfer: Transfer) -> Result { - if transfer.poll() { - // Here we could implement all features. - Ok(transfer) - } else { - warn!("Unfinished transfer in post processing. Returning buffer to queue. This will need explicit cleanup."); - Err(VirtioNetError::ProcessOngoing) - } + fn post_processing(buffer_tkn: Box) -> Result, VirtioNetError> { + Ok(buffer_tkn) } /// Adds a given queue to the underlying vector and populates the queue with RecvBuffers. @@ -219,7 +209,7 @@ impl RxQueues { // Transfers will be awaited at the queue buff_tkn .provide() - .dispatch_await(Rc::clone(&self.poll_queue), false); + .dispatch_await(self.poll_sender.clone(), false); } // Safe virtqueue @@ -230,15 +220,17 @@ impl RxQueues { } } - fn get_next(&mut self) -> Option { - let transfer = self.poll_queue.borrow_mut().pop_front(); + fn get_next(&mut self) -> Option> { + let transfer = self.poll_receiver.try_recv(); - transfer.or_else(|| { - // Check if any not yet provided transfers are in the queue. - self.poll(); + transfer + .or_else(|_| { + // Check if any not yet provided transfers are in the queue. + self.poll(); - self.poll_queue.borrow_mut().pop_front() - }) + self.poll_receiver.try_recv() + }) + .ok() } fn poll(&self) { @@ -276,7 +268,8 @@ impl RxQueues { /// to the respective queue structures. pub struct TxQueues { vqs: Vec>, - poll_queue: Rc>>, + poll_sender: async_channel::Sender>, + poll_receiver: async_channel::Receiver>, ready_queue: Vec, /// Indicates, whether the Driver/Device are using multiple /// queues for communication. @@ -284,15 +277,12 @@ pub struct TxQueues { } impl TxQueues { - pub fn new( - vqs: Vec>, - poll_queue: Rc>>, - ready_queue: Vec, - is_multi: bool, - ) -> Self { + pub fn new(vqs: Vec>, ready_queue: Vec, is_multi: bool) -> Self { + let (poll_sender, poll_receiver) = async_channel::unbounded(); Self { vqs, - poll_queue, + poll_sender, + poll_receiver, ready_queue, is_multi, } @@ -415,12 +405,12 @@ impl TxQueues { } } - if self.poll_queue.borrow().is_empty() { + if self.poll_receiver.is_empty() { self.poll(); } - while let Some(transfer) = self.poll_queue.borrow_mut().pop_back() { - let mut tkn = transfer.reuse().unwrap(); + while let Ok(buffer_token) = self.poll_receiver.try_recv() { + let mut tkn = buffer_token.reset(); let (send_len, _) = tkn.len(); match send_len.cmp(&len) { @@ -491,7 +481,7 @@ impl NetworkDriver for VirtioNetDriver { #[allow(dead_code)] fn has_packet(&self) -> bool { self.recv_vqs.poll(); - !self.recv_vqs.poll_queue.borrow().is_empty() + !self.recv_vqs.poll_receiver.is_empty() } /// Provides smoltcp a slice to copy the IP packet and transfer the packet @@ -554,7 +544,7 @@ impl NetworkDriver for VirtioNetDriver { buff_tkn .provide() - .dispatch_await(Rc::clone(&self.send_vqs.poll_queue), false); + .dispatch_await(self.send_vqs.poll_sender.clone(), false); result } else { @@ -586,10 +576,9 @@ impl NetworkDriver for VirtioNetDriver { // drop packets with invalid packet size if packet.len() < HEADER_SIZE { transfer - .reuse() - .unwrap() + .reset() .provide() - .dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false); + .dispatch_await(self.recv_vqs.poll_sender.clone(), false); return None; } @@ -604,10 +593,9 @@ impl NetworkDriver for VirtioNetDriver { vec_data.extend_from_slice(&packet[mem::size_of::()..]); transfer - .reuse() - .unwrap() + .reset() .provide() - .dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false); + .dispatch_await(self.recv_vqs.poll_sender.clone(), false); num_buffers }; @@ -627,22 +615,20 @@ impl NetworkDriver for VirtioNetDriver { let packet = recv_data.pop().unwrap(); vec_data.extend_from_slice(packet); transfer - .reuse() - .unwrap() + .reset() .provide() - .dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false); + .dispatch_await(self.recv_vqs.poll_sender.clone(), false); } Some((RxToken::new(vec_data), TxToken::new())) } else { error!("Empty transfer, or with wrong buffer layout. Reusing and returning error to user-space network driver..."); transfer - .reuse() - .unwrap() + .reset() .write_seq(None::<&VirtioNetHdr>, Some(&VirtioNetHdr::default())) .unwrap() .provide() - .dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false); + .dispatch_await(self.recv_vqs.poll_sender.clone(), false); None } diff --git a/src/drivers/net/virtio_pci.rs b/src/drivers/net/virtio_pci.rs index f1852138f8..5559c6c009 100644 --- a/src/drivers/net/virtio_pci.rs +++ b/src/drivers/net/virtio_pci.rs @@ -2,10 +2,7 @@ //! //! The module contains ... -use alloc::collections::VecDeque; -use alloc::rc::Rc; use alloc::vec::Vec; -use core::cell::RefCell; use core::str::FromStr; use smoltcp::phy::ChecksumCapabilities; @@ -146,13 +143,8 @@ impl VirtioNetDriver { notif_cfg, ctrl_vq: CtrlQueue::new(None), - recv_vqs: RxQueues::new(Vec::new(), Rc::new(RefCell::new(VecDeque::new())), false), - send_vqs: TxQueues::new( - Vec::new(), - Rc::new(RefCell::new(VecDeque::new())), - Vec::new(), - false, - ), + recv_vqs: RxQueues::new(Vec::new(), false), + send_vqs: TxQueues::new(Vec::new(), Vec::new(), false), num_vqs: 0, irq: device.get_irq().unwrap(), mtu, diff --git a/src/drivers/virtio/virtqueue/mod.rs b/src/drivers/virtio/virtqueue/mod.rs index d254b2e0bc..72b75529ba 100644 --- a/src/drivers/virtio/virtqueue/mod.rs +++ b/src/drivers/virtio/virtqueue/mod.rs @@ -14,7 +14,6 @@ pub mod packed; pub mod split; use alloc::boxed::Box; -use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::RefCell; @@ -22,6 +21,7 @@ use core::ops::{BitAnd, Deref, DerefMut}; use core::ptr; use align_address::Align; +use async_channel::TryRecvError; use zerocopy::AsBytes; use self::error::{BufferError, VirtqError}; @@ -99,6 +99,8 @@ struct Descriptor { flags: u16, } +type BufferTokenSender = async_channel::Sender>; + // Public interface of Virtq /// The Virtq trait unifies access to the two different Virtqueue types @@ -129,7 +131,6 @@ pub trait Virtq: VirtqPrivate { /// /// * `TransferTokens` which hold an `await_queue` will be placed into /// these queues. - /// * All finished `TransferTokens` will have a state of `TransferState::Finished`. fn poll(&self); /// Dispatches a batch of transfer token. The buffers of the respective transfers are provided to the queue in @@ -156,7 +157,7 @@ pub trait Virtq: VirtqPrivate { fn dispatch_batch_await( &self, tkns: Vec, - await_queue: Rc>>, + await_queue: BufferTokenSender, notif: bool, ); @@ -188,10 +189,10 @@ pub trait Virtq: VirtqPrivate { /// * Data behind the respective raw pointers will NOT be deallocated. Under no circumstances. /// * Calley is responsible for ensuring the raw pointers will remain valid from start till end of transfer. /// * start: call of `fn prep_transfer_from_raw()` - /// * end: return of the [Transfer] via [TransferToken::dispatch_blocking] or its push to the [TransferToken::await_queue]. + /// * end: return of the [BufferToken] via [TransferToken::dispatch_blocking] or its push to the [TransferToken::await_queue]. /// * In case the underlying BufferToken is reused, the raw pointers MUST still be valid all the time - /// BufferToken exists. - /// * Transfer created from this TransferTokens will ONLY allow to return a copy of the data. + /// [BufferToken] exists. + /// * [BufferToken] created from this TransferTokens will ONLY allow to return a copy of the data. /// * This is due to the fact, that the `Transfer.ret()` returns a `Box[u8]`, which must own /// the array. This would lead to unwanted frees, if not handled carefully /// * Drivers must take care of keeping a copy of the respective `*mut T` and `*mut K` for themselves @@ -280,7 +281,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Single { desc_lst: vec![desc].into_boxed_slice(), @@ -320,7 +320,6 @@ pub trait Virtq: VirtqPrivate { } Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Multiple { desc_lst: desc_lst.into_boxed_slice(), @@ -364,7 +363,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Indirect { desc_lst: desc_lst.into_boxed_slice(), @@ -400,7 +398,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: None, recv_buff: Some(Buffer::Single { @@ -440,7 +437,6 @@ pub trait Virtq: VirtqPrivate { } Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: None, recv_buff: Some(Buffer::Multiple { @@ -484,7 +480,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: None, recv_buff: Some(Buffer::Indirect { @@ -533,7 +528,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Single { desc_lst: vec![send_desc].into_boxed_slice(), @@ -591,7 +585,6 @@ pub trait Virtq: VirtqPrivate { } Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Single { desc_lst: vec![send_desc].into_boxed_slice(), @@ -659,7 +652,6 @@ pub trait Virtq: VirtqPrivate { } Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Multiple { desc_lst: send_desc_lst.into_boxed_slice(), @@ -717,7 +709,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { send_buff: Some(Buffer::Multiple { desc_lst: send_desc_lst.into_boxed_slice(), @@ -790,7 +781,6 @@ pub trait Virtq: VirtqPrivate { }; Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(BufferToken { recv_buff: Some(Buffer::Indirect { desc_lst: recv_desc_lst.into_boxed_slice(), @@ -1397,11 +1387,7 @@ pub fn dispatch_batch(tkns: Vec, notif: bool) { /// The `notif` parameter indicates if the driver wants to have a notification for this specific /// transfer. This is only for performance optimization. As it is NOT ensured, that the device sees the /// updated notification flags before finishing transfers! -pub fn dispatch_batch_await( - tkns: Vec, - await_queue: Rc>>, - notif: bool, -) { +pub fn dispatch_batch_await(tkns: Vec, await_queue: BufferTokenSender, notif: bool) { let mut used_vqs: Vec<(Rc, Vec)> = Vec::new(); // Sort the TransferTokens depending in the queue their coming from. @@ -1432,7 +1418,7 @@ pub fn dispatch_batch_await( } for (vq, tkn_lst) in used_vqs { - vq.dispatch_batch_await(tkn_lst, Rc::clone(&await_queue), notif); + vq.dispatch_batch_await(tkn_lst, await_queue.clone(), notif); } } @@ -1478,419 +1464,9 @@ pub trait AsSliceU8 { } } -/// The [Transfer] will be received when a [TransferToken] is dispatched via [TransferToken::dispatch_blocking] or through the await -/// queue given to [TransferToken::dispatch_await]. -/// -/// The struct represents an ongoing transfer or an active transfer. While this does NOT mean, that the transfer is at all times inside -/// actual virtqueue. The Transfers state can be polled via `Transfer.poll()`, which returns a bool if the transfer is finished. -/// -/// **Finished Transfers:** -/// * Finished transfers are able to return their send and receive buffers. Either as a copy via `Transfer.ret_cpy()` or as the actual -/// buffers via `Transfer.ret()`. -/// * Finished transfers can be reused via `Transfer.reuse()`. -/// * This returns an normal BufferToken (One should be cautious with reusing transfers where buffers were created from raw pointers) -pub struct Transfer { - transfer_tkn: Option>, -} - -// Public Interface of Transfer -impl Transfer { - /// Used to poll the current state of the transfer. - /// * true = Transfer is finished and can be closed, reused or return data - /// * false = Transfer is ongoing - pub fn poll(&self) -> bool { - // Unwrapping is okay here, as Transfers must hold a TransferToken - match self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => true, - TransferState::Ready => unreachable!("Transfers owned by other than queue should have Tokens, of Finished or Processing State!"), - TransferState::Processing => false, - } - } - - /// Returns a vector of immutable slices to the underlying memory areas. - /// - /// The vectors contain the slices in creation order. - /// E.g.: - /// * Driver creates buffer as - /// * send buffer: 50 bytes, 60 bytes - /// * receive buffer: 10 bytes - /// * The return tuple will be: - /// * `(Some(vec[50, 60]), Some(vec[10]))` - /// * Where 50 refers to a slice of u8 of length 50. - /// The other numbers follow the same principle. - pub fn as_slices(&self) -> Result<(Option>, Option>), VirtqError> { - match &self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - // Unwrapping is okay here, as TransferToken must hold a BufferToken - let send_data = match &self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .send_buff - { - Some(buff) => { - let mut arr = Vec::with_capacity(buff.as_slice().len()); - - for desc in buff.as_slice() { - arr.push(desc.deref()) - } - - Some(arr) - } - None => None, - }; - - let recv_data = match &self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .recv_buff - { - Some(buff) => { - let mut arr = Vec::with_capacity(buff.as_slice().len()); - - for desc in buff.as_slice() { - arr.push(desc.deref()) - } - - Some(arr) - } - None => None, - }; - - Ok((send_data, recv_data)) - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(None)), - TransferState::Ready => unreachable!( - "Transfers not owned by a queue Must have state Finished or Processing!" - ), - } - } - - /// Returns a vector of mutable slices to the underlying memory areas. - /// - /// The vectors contain the slices in creation order. - /// E.g.: - /// * Driver creates buffer as - /// * send buffer: 50 bytes, 60 bytes - /// * receive buffer: 10 bytes - /// * The return tuple will be: - /// * `(Some(vec[50, 60]), Some(vec[10]))` - /// * Where 50 refers to a slice of u8 of length 50. - /// The other numbers follow the same principle. - pub fn as_slices_mut( - &mut self, - ) -> Result<(Option>, Option>), VirtqError> { - match &self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - let (send_buff, recv_buff) = { - let BufferToken { - send_buff, - recv_buff, - .. - } = self - .transfer_tkn - .as_mut() - .unwrap() - .buff_tkn - .as_mut() - .unwrap(); - - (send_buff.as_mut(), recv_buff.as_mut()) - }; - - // Unwrapping is okay here, as TransferToken must hold a BufferToken - let send_data = match send_buff { - Some(buff) => { - let mut arr = Vec::with_capacity(buff.as_slice().len()); - - for desc in buff.as_mut_slice() { - arr.push(desc.deref_mut()) - } - - Some(arr) - } - None => None, - }; - - let recv_data = match recv_buff { - Some(buff) => { - let mut arr = Vec::with_capacity(buff.as_slice().len()); - - for desc in buff.as_mut_slice() { - arr.push(desc.deref_mut()) - } - - Some(arr) - } - None => None, - }; - - Ok((send_data, recv_data)) - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(None)), - TransferState::Ready => unreachable!( - "Transfers not owned by a queue Must have state Finished or Processing!" - ), - } - } - - /// Returns a copy if the respective send and receiving buffers - /// The actual buffers remain in the BufferToken and hence the token can be - /// reused afterwards. - /// - /// **Return Tuple** - /// - /// `(sended_data, received_data)` - /// - /// The returned data is of type `Box<[Box<[u8]>]>`. This function therefore preserves - /// the scattered structure of the buffer, - /// - /// If one creates this buffer via a `Virtq.prep_transfer_from_raw()` - /// call, a casting back to the original structure `T` is NOT possible. - /// In these cases please use `Transfer.ret_cpy()` or use 'BuffSpec::Single' only! - pub fn ret_scat_cpy( - &self, - ) -> Result<(Option>>, Option>>), VirtqError> { - match &self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - // Unwrapping is okay here, as TransferToken must hold a BufferToken - let send_data = self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .send_buff - .as_ref() - .map(Buffer::scat_cpy); - - let recv_data = self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .recv_buff - .as_ref() - .map(Buffer::scat_cpy); - - Ok((send_data, recv_data)) - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(None)), - TransferState::Ready => unreachable!( - "Transfers not owned by a queue Must have state Finished or Processing!" - ), - } - } - - /// Returns a copy if the respective send and receiving buffers - /// The actual buffers remain in the BufferToken and hence the token can be - /// reused afterwards. - /// - /// **Return Tuple** - /// - /// `(sended_data, received_data)` - /// - /// The sended_data is `Box<[u8]>`. This function herefore merges (if multiple descriptors - /// were requested for one buffer) into a single `[u8]`. - /// - /// It can be assumed, that if one created the send buffer from a structure `T`, that - /// `&sended_data[0] as *const u8 == *const T` - pub fn ret_cpy(&self) -> Result<(Option>, Option>), VirtqError> { - match &self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - // Unwrapping is okay here, as TransferToken must hold a BufferToken - let send_data = self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .send_buff - .as_ref() - .map(Buffer::cpy); - - let recv_data = self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .recv_buff - .as_ref() - .map(Buffer::cpy); - - Ok((send_data, recv_data)) - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(None)), - TransferState::Ready => unreachable!( - "Transfers not owned by a queue Must have state Finished or Processing!" - ), - } - } - - /// # HIGHLY EXPERIMENTIALLY - /// This function returns a Vector of tuples to the allocated memory areas Currently the complete behaviour of this function is not well tested and it should be used with care. - /// - /// **INFO:** - /// * Memory regions MUST be deallocated via `Virtq::free_raw(*mut u8, len)` - /// * Memory regions length might be larger than expected due to the used - /// allocation function in the kernel. Hence one MUST NOT assume valid data - /// after the length of the buffer, that was given at creation, is reached. - /// * Still the provided `Virtq::free_raw(*mut u8, len)` function MUST be provided - /// with the actual usize returned by this function in order to prevent memory leaks or failure. - /// * Fails if `TransferState != Finished`. - /// - pub fn into_raw( - mut self, - ) -> Result<(Option>, Option>), VirtqError> { - let state = self.transfer_tkn.as_ref().unwrap().state; - - match state { - TransferState::Finished => { - // Desctructure Token - let mut transfer_tkn = self.transfer_tkn.take().unwrap(); - - let mut buffer_tkn = transfer_tkn.buff_tkn.take().unwrap(); - - let send_data = if buffer_tkn.ret_send { - match buffer_tkn.send_buff { - Some(buff) => { - // This data is not a second time returnable - // Unnecessary, because token will be dropped. - // But to be consistent in state. - buffer_tkn.ret_send = false; - Some(buff.into_raw()) - } - None => None, - } - } else { - return Err(VirtqError::NoReuseBuffer); - }; - - let recv_data = if buffer_tkn.ret_recv { - match buffer_tkn.recv_buff { - Some(buff) => { - // This data is not a second time returnable - // Unnecessary, because token will be dropped. - // But to be consistent in state. - buffer_tkn.ret_recv = false; - Some(buff.into_raw()) - } - None => None, - } - } else { - return Err(VirtqError::NoReuseBuffer); - }; - // Prevent Token to be reusable although it will be dropped - // later in this function. - // Unnecessary but to be consistent in state. - // - // Unwrapping is okay here, as TransferToken must hold a BufferToken - buffer_tkn.reusable = false; - - Ok((send_data, recv_data)) - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(Some(self))), - TransferState::Ready => unreachable!( - "Transfers not owned by a queue Must have state Finished or Processing!" - ), - } - } - - /// If the transfer was finished returns the BufferToken inside the transfer else returns an error. - /// - /// **WARN:** - /// - /// This function does restore the actual size of the Buffer at creation but does NOT reset the - /// written memory areas to zero! If this is needed please use `Transfer.reuse_reset` - pub fn reuse(mut self) -> Result { - // Unwrapping is okay here, as TransferToken must hold a BufferToken - match self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - if self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .reusable - { - let tkn = self.transfer_tkn.take().unwrap().buff_tkn.take().unwrap(); - - Ok(tkn.reset()) - } else { - Err(VirtqError::NoReuseBuffer) - } - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(Some(self))), - TransferState::Ready => unreachable!( - "Transfers coming from outside the queue must be Processing or Finished" - ), - } - } - - /// If the transfer was finished returns the BufferToken inside the transfer else returns an error. - /// - /// - /// This function does restore the actual size of the Buffer at creation and does reset the - /// written memory areas to zero! Depending on the size of the Buffer this might take some time and - /// one could prefer to allocate a new token via prep_buffer() of the wanted size. - pub fn reuse_reset(mut self) -> Result { - // Unwrapping is okay here, as TransferToken must hold a BufferToken - match self.transfer_tkn.as_ref().unwrap().state { - TransferState::Finished => { - if self - .transfer_tkn - .as_ref() - .unwrap() - .buff_tkn - .as_ref() - .unwrap() - .reusable - { - let tkn = self.transfer_tkn.take().unwrap().buff_tkn.take().unwrap(); - - Ok(tkn.reset_purge()) - } else { - Err(VirtqError::NoReuseBuffer) - } - } - TransferState::Processing => Err(VirtqError::OngoingTransfer(Some(self))), - TransferState::Ready => unreachable!( - "Transfers coming from outside the queue must be Processing or Finished" - ), - } - } -} - -/// Enum indicates the current state of a transfer. -#[derive(PartialEq, Copy, Clone, Debug)] -enum TransferState { - /// Queue finished transfer - Finished, - /// Transfer is ongoing and still processed by queue - Processing, - /// Transfer is ready to be sended - Ready, -} - /// The struct represents buffers which are ready to be send via the /// virtqueue. Buffers can no longer be written or retrieved. pub struct TransferToken { - state: TransferState, /// Must be some in order to prevent drop /// upon reuse. buff_tkn: Option, @@ -1898,7 +1474,7 @@ pub struct TransferToken { /// If Some, finished TransferTokens will be placed here /// as finished `Transfers`. If None, only the state /// of the Token will be changed. - await_queue: Option>>>, + await_queue: Option, } /// Public Interface for TransferToken @@ -1914,8 +1490,8 @@ impl TransferToken { /// The `notif` parameter indicates if the driver wants to have a notification for this specific /// transfer. This is only for performance optimization. As it is NOT ensured, that the device sees the /// updated notification flags before finishing transfers! - pub fn dispatch_await(mut self, await_queue: Rc>>, notif: bool) { - self.await_queue = Some(Rc::clone(&await_queue)); + pub fn dispatch_await(mut self, await_queue: BufferTokenSender, notif: bool) { + self.await_queue = Some(await_queue.clone()); self.get_vq().dispatch(self, notif); } @@ -1932,29 +1508,35 @@ impl TransferToken { /// Dispatches the provided TransferToken to the respectuve queue and does /// return when, the queue finished the transfer. /// - /// The resultaing [TransferState] in this case is of course - /// finished and the returned [Transfer] can be reused, copied from + /// The returned [BufferToken] can be reused, copied from /// or return the underlying buffers. /// /// **INFO:** /// Currently this function is constantly polling the queue while keeping the notifications disabled. /// Upon finish notifications are enabled again. - pub fn dispatch_blocking(self) -> Result { + pub fn dispatch_blocking(self) -> Result, VirtqError> { let vq = self.get_vq(); - let rcv_queue = Rc::new(RefCell::new(VecDeque::with_capacity(1))); - self.dispatch_await(rcv_queue.clone(), false); + let (sender, receiver) = async_channel::bounded(1); + self.dispatch_await(sender, false); vq.disable_notifs(); - while rcv_queue.borrow().is_empty() { - // Keep Spinning until the receive queue is filled - vq.poll() + let result: Box; + // Keep Spinning until the receive queue is filled + loop { + match receiver.try_recv() { + Ok(buffer_tkn) => { + result = buffer_tkn; + break; + } + Err(TryRecvError::Closed) => return Err(VirtqError::General), + Err(TryRecvError::Empty) => vq.poll(), + } } vq.enable_notifs(); - let result = Ok(rcv_queue.borrow_mut().pop_front().unwrap()); - result + Ok(result) } } @@ -2143,7 +1725,7 @@ impl BufferToken { /// * Resetting the write status inside the MemDescr. -> Allowing to rewrite the buffers /// * Resetting the MemDescr length at initialization. This length might be reduced upon writes /// of the driver or the device. - fn reset(mut self) -> Self { + pub fn reset(mut self) -> Self { let mut ctrl_desc_cnt = 0usize; if let Some(buff) = self.send_buff.as_mut() { @@ -2396,6 +1978,7 @@ impl BufferToken { (None, None) => unreachable!("Empty BufferToken not allowed!"), } } + /// Returns the underlying raw pointers to the user accessible memory hold by the Buffertoken. This is mostly /// useful in order to provide the user space with pointers to write to. Return tuple has the form /// (`pointer_to_mem_area`, `length_of_accessible_mem_area`). @@ -2442,6 +2025,102 @@ impl BufferToken { } } + /// Returns a vector of immutable slices to the underlying memory areas. + /// + /// The vectors contain the slices in creation order. + /// E.g.: + /// * Driver creates buffer as + /// * send buffer: 50 bytes, 60 bytes + /// * receive buffer: 10 bytes + /// * The return tuple will be: + /// * `(Some(vec[50, 60]), Some(vec[10]))` + /// * Where 50 refers to a slice of u8 of length 50. + /// The other numbers follow the same principle. + pub fn as_slices(&self) -> Result<(Option>, Option>), VirtqError> { + // Unwrapping is okay here, as TransferToken must hold a BufferToken + let send_data = match &self.send_buff { + Some(buff) => { + let mut arr = Vec::with_capacity(buff.as_slice().len()); + + for desc in buff.as_slice() { + arr.push(desc.deref()) + } + + Some(arr) + } + None => None, + }; + + let recv_data = match &self.recv_buff { + Some(buff) => { + let mut arr = Vec::with_capacity(buff.as_slice().len()); + + for desc in buff.as_slice() { + arr.push(desc.deref()) + } + + Some(arr) + } + None => None, + }; + + Ok((send_data, recv_data)) + } + + /// Returns a vector of mutable slices to the underlying memory areas. + /// + /// The vectors contain the slices in creation order. + /// E.g.: + /// * Driver creates buffer as + /// * send buffer: 50 bytes, 60 bytes + /// * receive buffer: 10 bytes + /// * The return tuple will be: + /// * `(Some(vec[50, 60]), Some(vec[10]))` + /// * Where 50 refers to a slice of u8 of length 50. + /// The other numbers follow the same principle. + pub fn as_slices_mut( + &mut self, + ) -> Result<(Option>, Option>), VirtqError> { + let (send_buff, recv_buff) = { + let BufferToken { + send_buff, + recv_buff, + .. + } = self; + + (send_buff.as_mut(), recv_buff.as_mut()) + }; + + // Unwrapping is okay here, as TransferToken must hold a BufferToken + let send_data = match send_buff { + Some(buff) => { + let mut arr = Vec::with_capacity(buff.as_slice().len()); + + for desc in buff.as_mut_slice() { + arr.push(desc.deref_mut()) + } + + Some(arr) + } + None => None, + }; + + let recv_data = match recv_buff { + Some(buff) => { + let mut arr = Vec::with_capacity(buff.as_slice().len()); + + for desc in buff.as_mut_slice() { + arr.push(desc.deref_mut()) + } + + Some(arr) + } + None => None, + }; + + Ok((send_data, recv_data)) + } + /// Writes the provided datastructures into the respective buffers. `K` into `self.send_buff` and `H` into /// `self.recv_buff`. /// If the provided datastructures do not "fit" into the respective buffers, the function will return an error. Even @@ -2520,7 +2199,6 @@ impl BufferToken { } Ok(TransferToken { - state: TransferState::Ready, buff_tkn: Some(self), await_queue: None, }) @@ -2583,7 +2261,6 @@ impl BufferToken { /// After this call, the buffers are no longer writable. pub fn provide(self) -> TransferToken { TransferToken { - state: TransferState::Ready, buff_tkn: Some(self), await_queue: None, } @@ -3306,7 +2983,6 @@ impl From for u16 { /// This module unifies errors provided to useres of a virtqueue, independent of the underlying /// virtqueue implementation, realized via the different enum variants. pub mod error { - use super::Transfer; use crate::fd; #[derive(Debug)] @@ -3348,13 +3024,6 @@ pub mod error { /// via `Virtq.prep_transfer_from_raw()`. Due to the fact, that reusing /// Buffers which refer to raw pointers seems dangerours, this is forbidden. NoReuseBuffer, - /// Indicates that a Transfer method was called, that is only allowed to be - /// called when the transfer is Finished (or Ready, although this state is - /// only allowed for Transfer structs owned by the Virtqueue). - /// The Error returns the called Transfer for recovery, if called from a - /// consuming function as a `Some(Transfer)`. For non-consuming - /// functions returns `None`. - OngoingTransfer(Option), /// Indicates a write into a Buffer that is not existing NoBufferAvail, /// Indicates that a write to a Buffer happened and the data to be written into @@ -3381,7 +3050,6 @@ pub mod error { VirtqError::NoDescrAvail => write!(f, "Virtqs memory pool is exhausted!"), VirtqError::BufferSizeWrong(_) => write!(f, "Specified Buffer is to small for write!"), VirtqError::NoReuseBuffer => write!(f, "Buffer can not be reused!"), - VirtqError::OngoingTransfer(_) => write!(f, "Transfer is ongoging and can not be used currently!"), VirtqError::WriteTooLarge => write!(f, "Write is to large for BufferToken!"), VirtqError::BufferToLarge => write!(f, "Buffer to large for queue! u32::MAX exceeded."), VirtqError::QueueSizeNotAllowed(_) => write!(f, "The requested queue size is not valid."), diff --git a/src/drivers/virtio/virtqueue/packed.rs b/src/drivers/virtio/virtqueue/packed.rs index 5fc1467a7c..d7edab90a4 100644 --- a/src/drivers/virtio/virtqueue/packed.rs +++ b/src/drivers/virtio/virtqueue/packed.rs @@ -3,7 +3,6 @@ #![allow(dead_code)] use alloc::boxed::Box; -use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::RefCell; @@ -20,8 +19,8 @@ use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl}; use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl}; use super::error::VirtqError; use super::{ - BuffSpec, Buffer, BufferToken, Bytes, DescrFlags, MemDescr, MemPool, Transfer, TransferState, - TransferToken, Virtq, VirtqPrivate, VqIndex, VqSize, + BuffSpec, Buffer, BufferToken, Bytes, DescrFlags, MemDescr, MemPool, TransferToken, Virtq, + VirtqPrivate, VqIndex, VqSize, }; use crate::arch::mm::paging::{BasePageSize, PageSize}; use crate::arch::mm::{paging, VirtAddr}; @@ -131,17 +130,14 @@ impl DescriptorRing { } /// Polls poll index and sets the state of any finished TransferTokens. - /// If [TransferToken::await_queue] is available, the Transfer will be moved to the queue. + /// If [TransferToken::await_queue] is available, the [BufferToken] will be moved to the queue. fn poll(&mut self) { let mut ctrl = self.get_read_ctrler(); if let Some(mut tkn) = ctrl.poll_next() { - tkn.state = TransferState::Finished; if let Some(queue) = tkn.await_queue.take() { // Place the TransferToken in a Transfer, which will hold ownership of the token - queue.borrow_mut().push_back(Transfer { - transfer_tkn: Some(tkn), - }); + queue.try_send(Box::new(tkn.buff_tkn.unwrap())).unwrap(); } } } @@ -154,7 +150,7 @@ impl DescriptorRing { let mut first_ctrl_settings: (usize, u16, WrapCount) = (0, 0, WrapCount::new()); let mut first_buffer = None; - for (i, mut tkn) in tkn_lst.into_iter().enumerate() { + for (i, tkn) in tkn_lst.into_iter().enumerate() { // Check length and if its fits. This should always be true due to the restriction of // the memory pool, but to be sure. assert!(tkn.buff_tkn.as_ref().unwrap().num_consuming_descr() <= self.capacity); @@ -264,8 +260,6 @@ impl DescriptorRing { (None, None) => unreachable!("Empty Transfers are not allowed!"), // This should already be caught at creation of BufferToken } - tkn.state = TransferState::Processing; - if i == 0 { first_ctrl_settings = (ctrl.start, ctrl.buff_id, ctrl.wrap_at_init); first_buffer = Some(Box::new(tkn)); @@ -288,7 +282,7 @@ impl DescriptorRing { (first_ctrl_settings.0, first_ctrl_settings.2 .0 as u8) } - fn push(&mut self, mut tkn: TransferToken) -> (usize, u8) { + fn push(&mut self, tkn: TransferToken) -> (usize, u8) { // Check length and if its fits. This should always be true due to the restriction of // the memory pool, but to be sure. assert!(tkn.buff_tkn.as_ref().unwrap().num_consuming_descr() <= self.capacity); @@ -391,10 +385,6 @@ impl DescriptorRing { (None, None) => unreachable!("Empty Transfers are not allowed!"), // This should already be caught at creation of BufferToken } - fence(Ordering::SeqCst); - // Update the state of the actual Token - tkn.state = TransferState::Processing; - fence(Ordering::SeqCst); // Update flags of the first descriptor and set new write_index ctrl.make_avail(Box::new(tkn)); @@ -1003,7 +993,7 @@ impl Virtq for PackedVq { fn dispatch_batch_await( &self, mut tkns: Vec, - await_queue: Rc>>, + await_queue: super::BufferTokenSender, notif: bool, ) { // Zero transfers are not allowed @@ -1011,7 +1001,7 @@ impl Virtq for PackedVq { // We have to iterate here too, in order to ensure, tokens are placed into the await_queue for tkn in tkns.iter_mut() { - tkn.await_queue = Some(Rc::clone(&await_queue)); + tkn.await_queue = Some(await_queue.clone()); } let (next_off, next_wrap) = self.descr_ring.borrow_mut().push_batch(tkns); diff --git a/src/drivers/virtio/virtqueue/split.rs b/src/drivers/virtio/virtqueue/split.rs index 0ee7d878d6..0d1cc81c54 100644 --- a/src/drivers/virtio/virtqueue/split.rs +++ b/src/drivers/virtio/virtqueue/split.rs @@ -3,7 +3,6 @@ #![allow(dead_code)] use alloc::boxed::Box; -use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec::Vec; use core::alloc::{Allocator, Layout}; @@ -21,8 +20,8 @@ use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl}; use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl}; use super::error::VirtqError; use super::{ - BuffSpec, BufferToken, Bytes, DescrFlags, MemDescr, MemPool, Transfer, TransferState, - TransferToken, Virtq, VirtqPrivate, VqIndex, VqSize, + BuffSpec, BufferToken, Bytes, DescrFlags, MemDescr, MemPool, TransferToken, Virtq, + VirtqPrivate, VqIndex, VqSize, }; use crate::arch::memory_barrier; use crate::arch::mm::{paging, VirtAddr}; @@ -304,11 +303,8 @@ impl DescrRing { .restr_size(None, Some(used_elem.len.get() as usize)) .unwrap(); } - tkn.state = TransferState::Finished; if let Some(queue) = tkn.await_queue.take() { - queue.borrow_mut().push_back(Transfer { - transfer_tkn: Some(tkn), - }) + queue.try_send(Box::new(tkn.buff_tkn.unwrap())).unwrap() } memory_barrier(); self.read_idx = self.read_idx.wrapping_add(1); @@ -364,7 +360,7 @@ impl Virtq for SplitVq { fn dispatch_batch_await( &self, _tkns: Vec, - _await_queue: Rc>>, + _await_queue: super::BufferTokenSender, _notif: bool, ) { unimplemented!()