Browse Source

Merge pull request #85 from rcore-os/dmaalloc

Use heap rather than DMA buffer for buffers which will be added to queues
Andrew Walbran 1 year ago
parent
commit
a8cf6f5550
7 changed files with 49 additions and 44 deletions
  1. 1 1
      Cargo.toml
  2. 17 13
      src/device/console.rs
  3. 12 19
      src/device/gpu.rs
  4. 2 0
      src/device/mod.rs
  5. 2 0
      src/device/socket/mod.rs
  6. 13 11
      src/device/socket/vsock.rs
  7. 2 0
      src/lib.rs

+ 1 - 1
Cargo.toml

@@ -21,4 +21,4 @@ zerocopy = "0.6.1"
 
 [features]
 default = ["alloc"]
-alloc = []
+alloc = ["zerocopy/alloc"]

+ 17 - 13
src/device/console.rs

@@ -1,10 +1,11 @@
 //! Driver for VirtIO console devices.
 
-use crate::hal::{BufferDirection, Dma, Hal};
+use crate::hal::Hal;
 use crate::queue::VirtQueue;
 use crate::transport::Transport;
 use crate::volatile::{volread, ReadOnly, WriteOnly};
-use crate::Result;
+use crate::{Result, PAGE_SIZE};
+use alloc::boxed::Box;
 use bitflags::bitflags;
 use core::ptr::NonNull;
 use log::info;
@@ -38,13 +39,12 @@ const QUEUE_SIZE: usize = 2;
 /// # Ok(())
 /// # }
 /// ```
-pub struct VirtIOConsole<'a, H: Hal, T: Transport> {
+pub struct VirtIOConsole<H: Hal, T: Transport> {
     transport: T,
     config_space: NonNull<Config>,
     receiveq: VirtQueue<H, QUEUE_SIZE>,
     transmitq: VirtQueue<H, QUEUE_SIZE>,
-    queue_buf_dma: Dma<H>,
-    queue_buf_rx: &'a mut [u8],
+    queue_buf_rx: Box<[u8; PAGE_SIZE]>,
     cursor: usize,
     pending_len: usize,
     /// The token of the outstanding receive request, if there is one.
@@ -62,7 +62,7 @@ pub struct ConsoleInfo {
     pub max_ports: u32,
 }
 
-impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
+impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
     /// Creates a new VirtIO console driver.
     pub fn new(mut transport: T) -> Result<Self> {
         transport.begin_init(|features| {
@@ -74,12 +74,11 @@ impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
         let config_space = transport.config_space::<Config>()?;
         let receiveq = VirtQueue::new(&mut transport, QUEUE_RECEIVEQ_PORT_0)?;
         let transmitq = VirtQueue::new(&mut transport, QUEUE_TRANSMITQ_PORT_0)?;
-        let queue_buf_dma = Dma::new(1, BufferDirection::DeviceToDriver)?;
 
         // Safe because no alignment or initialisation is required for [u8], the DMA buffer is
         // dereferenceable, and the lifetime of the reference matches the lifetime of the DMA buffer
         // (which we don't otherwise access).
-        let queue_buf_rx = unsafe { queue_buf_dma.raw_slice().as_mut() };
+        let queue_buf_rx = Box::new([0; PAGE_SIZE]);
 
         transport.finish_init();
         let mut console = VirtIOConsole {
@@ -87,7 +86,6 @@ impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
             config_space,
             receiveq,
             transmitq,
-            queue_buf_dma,
             queue_buf_rx,
             cursor: 0,
             pending_len: 0,
@@ -118,7 +116,10 @@ impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
         if self.receive_token.is_none() && self.cursor == self.pending_len {
             // Safe because the buffer lasts at least as long as the queue, and there are no other
             // outstanding requests using the buffer.
-            self.receive_token = Some(unsafe { self.receiveq.add(&[], &mut [self.queue_buf_rx]) }?);
+            self.receive_token = Some(unsafe {
+                self.receiveq
+                    .add(&[], &mut [self.queue_buf_rx.as_mut_slice()])
+            }?);
             if self.receiveq.should_notify() {
                 self.transport.notify(QUEUE_RECEIVEQ_PORT_0);
             }
@@ -148,8 +149,11 @@ impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
                 // Safe because we are passing the same buffer as we passed to `VirtQueue::add` in
                 // `poll_retrieve` and it is still valid.
                 let len = unsafe {
-                    self.receiveq
-                        .pop_used(receive_token, &[], &mut [self.queue_buf_rx])?
+                    self.receiveq.pop_used(
+                        receive_token,
+                        &[],
+                        &mut [self.queue_buf_rx.as_mut_slice()],
+                    )?
                 };
                 flag = true;
                 assert_ne!(len, 0);
@@ -188,7 +192,7 @@ impl<H: Hal, T: Transport> VirtIOConsole<'_, H, T> {
     }
 }
 
-impl<H: Hal, T: Transport> Drop for VirtIOConsole<'_, H, T> {
+impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
     fn drop(&mut self) {
         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
         // after they have been freed.

+ 12 - 19
src/device/gpu.rs

@@ -4,7 +4,8 @@ use crate::hal::{BufferDirection, Dma, Hal};
 use crate::queue::VirtQueue;
 use crate::transport::Transport;
 use crate::volatile::{volread, ReadOnly, Volatile, WriteOnly};
-use crate::{pages, Error, Result};
+use crate::{pages, Error, Result, PAGE_SIZE};
+use alloc::boxed::Box;
 use bitflags::bitflags;
 use log::info;
 use zerocopy::{AsBytes, FromBytes};
@@ -18,7 +19,7 @@ const QUEUE_SIZE: u16 = 2;
 /// a gpu with 3D support on the host machine.
 /// In 2D mode the virtio-gpu device provides support for ARGB Hardware cursors
 /// and multiple scanouts (aka heads).
-pub struct VirtIOGpu<'a, H: Hal, T: Transport> {
+pub struct VirtIOGpu<H: Hal, T: Transport> {
     transport: T,
     rect: Option<Rect>,
     /// DMA area of frame buffer.
@@ -29,17 +30,13 @@ pub struct VirtIOGpu<'a, H: Hal, T: Transport> {
     control_queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
     /// Queue for sending cursor commands.
     cursor_queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
-    /// DMA region for sending data to the device.
-    dma_send: Dma<H>,
-    /// DMA region for receiving data from the device.
-    dma_recv: Dma<H>,
     /// Send buffer for queue.
-    queue_buf_send: &'a mut [u8],
+    queue_buf_send: Box<[u8]>,
     /// Recv buffer for queue.
-    queue_buf_recv: &'a mut [u8],
+    queue_buf_recv: Box<[u8]>,
 }
 
-impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
+impl<H: Hal, T: Transport> VirtIOGpu<H, T> {
     /// Create a new VirtIO-Gpu driver.
     pub fn new(mut transport: T) -> Result<Self> {
         transport.begin_init(|features| {
@@ -63,10 +60,8 @@ impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
         let control_queue = VirtQueue::new(&mut transport, QUEUE_TRANSMIT)?;
         let cursor_queue = VirtQueue::new(&mut transport, QUEUE_CURSOR)?;
 
-        let dma_send = Dma::new(1, BufferDirection::DriverToDevice)?;
-        let dma_recv = Dma::new(1, BufferDirection::DeviceToDriver)?;
-        let queue_buf_send = unsafe { dma_send.raw_slice().as_mut() };
-        let queue_buf_recv = unsafe { dma_recv.raw_slice().as_mut() };
+        let queue_buf_send = FromBytes::new_box_slice_zeroed(PAGE_SIZE);
+        let queue_buf_recv = FromBytes::new_box_slice_zeroed(PAGE_SIZE);
 
         transport.finish_init();
 
@@ -77,8 +72,6 @@ impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
             rect: None,
             control_queue,
             cursor_queue,
-            dma_send,
-            dma_recv,
             queue_buf_send,
             queue_buf_recv,
         })
@@ -177,8 +170,8 @@ impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
     fn request<Req: AsBytes, Rsp: FromBytes>(&mut self, req: Req) -> Result<Rsp> {
         req.write_to_prefix(&mut *self.queue_buf_send).unwrap();
         self.control_queue.add_notify_wait_pop(
-            &[self.queue_buf_send],
-            &mut [self.queue_buf_recv],
+            &[&self.queue_buf_send],
+            &mut [&mut self.queue_buf_recv],
             &mut self.transport,
         )?;
         Ok(Rsp::read_from_prefix(&*self.queue_buf_recv).unwrap())
@@ -188,7 +181,7 @@ impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
     fn cursor_request<Req: AsBytes>(&mut self, req: Req) -> Result {
         req.write_to_prefix(&mut *self.queue_buf_send).unwrap();
         self.cursor_queue.add_notify_wait_pop(
-            &[self.queue_buf_send],
+            &[&self.queue_buf_send],
             &mut [],
             &mut self.transport,
         )?;
@@ -286,7 +279,7 @@ impl<H: Hal, T: Transport> VirtIOGpu<'_, H, T> {
     }
 }
 
-impl<H: Hal, T: Transport> Drop for VirtIOGpu<'_, H, T> {
+impl<H: Hal, T: Transport> Drop for VirtIOGpu<H, T> {
     fn drop(&mut self) {
         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
         // after they have been freed.

+ 2 - 0
src/device/mod.rs

@@ -1,7 +1,9 @@
 //! Drivers for specific VirtIO devices.
 
 pub mod blk;
+#[cfg(feature = "alloc")]
 pub mod console;
+#[cfg(feature = "alloc")]
 pub mod gpu;
 #[cfg(feature = "alloc")]
 pub mod input;

+ 2 - 0
src/device/socket/mod.rs

@@ -2,7 +2,9 @@
 
 mod error;
 mod protocol;
+#[cfg(feature = "alloc")]
 mod vsock;
 
 pub use error::SocketError;
+#[cfg(feature = "alloc")]
 pub use vsock::{DisconnectReason, VirtIOSocket, VsockEvent, VsockEventType};

+ 13 - 11
src/device/socket/vsock.rs

@@ -4,11 +4,12 @@
 use super::error::SocketError;
 use super::protocol::{VirtioVsockConfig, VirtioVsockHdr, VirtioVsockOp, VsockAddr};
 use crate::device::common::Feature;
-use crate::hal::{BufferDirection, Dma, Hal};
+use crate::hal::Hal;
 use crate::queue::VirtQueue;
 use crate::transport::Transport;
 use crate::volatile::volread;
-use crate::Result;
+use crate::{Result, PAGE_SIZE};
+use alloc::boxed::Box;
 use core::hint::spin_loop;
 use core::mem::size_of;
 use core::ptr::NonNull;
@@ -108,7 +109,7 @@ pub struct VirtIOSocket<H: Hal, T: Transport> {
     /// The guest_cid field contains the guest’s context ID, which uniquely identifies
     /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
     guest_cid: u64,
-    rx_buf_dma: Dma<H>,
+    rx_buf: NonNull<[u8; PAGE_SIZE]>,
 
     /// Currently the device is only allowed to be connected to one destination at a time.
     connection_info: Option<ConnectionInfo>,
@@ -121,6 +122,10 @@ impl<H: Hal, T: Transport> Drop for VirtIOSocket<H, T> {
         self.transport.queue_unset(RX_QUEUE_IDX);
         self.transport.queue_unset(TX_QUEUE_IDX);
         self.transport.queue_unset(EVENT_QUEUE_IDX);
+
+        // Safe because we obtained the rx_buf pointer from Box::into_raw, and it won't be used
+        // anywhere else after the driver is destroyed.
+        unsafe { drop(Box::from_raw(self.rx_buf.as_ptr())) };
     }
 }
 
@@ -147,12 +152,9 @@ impl<H: Hal, T: Transport> VirtIOSocket<H, T> {
         let tx = VirtQueue::new(&mut transport, TX_QUEUE_IDX)?;
         let event = VirtQueue::new(&mut transport, EVENT_QUEUE_IDX)?;
 
-        // Allocates 4 KiB memory as the rx buffer.
-        let rx_buf_dma = Dma::new(
-            1, // pages
-            BufferDirection::DeviceToDriver,
-        )?;
-        let rx_buf = rx_buf_dma.raw_slice();
+        // Allocates 4 KiB memory for the RX buffer.
+        let rx_buf: NonNull<[u8; PAGE_SIZE]> =
+            NonNull::new(Box::into_raw(FromBytes::new_box_zeroed())).unwrap();
         // Safe because `rx_buf` lives as long as the `rx` queue.
         unsafe {
             Self::fill_rx_queue(&mut rx, rx_buf, &mut transport)?;
@@ -165,7 +167,7 @@ impl<H: Hal, T: Transport> VirtIOSocket<H, T> {
             tx,
             event,
             guest_cid,
-            rx_buf_dma,
+            rx_buf,
             connection_info: None,
         })
     }
@@ -482,7 +484,7 @@ impl<H: Hal, T: Transport> VirtIOSocket<H, T> {
         // buffer to `pop_used` as we previously passed to `add` for the token. Once we add the
         // buffer back to the RX queue then we don't access it again until next time it is popped.
         let header = unsafe {
-            let buffer = Self::as_mut_sub_rx_buffer(self.rx_buf_dma.raw_slice(), token.into())?;
+            let buffer = Self::as_mut_sub_rx_buffer(self.rx_buf, token.into())?;
             let _len = self.rx.pop_used(token, &[], &mut [buffer])?;
 
             // Read the header and body from the buffer. Don't check the result yet, because we need

+ 2 - 0
src/lib.rs

@@ -24,12 +24,14 @@
 //!
 //! ```
 //! # use virtio_drivers::Hal;
+//! # #[cfg(feature = "alloc")]
 //! use virtio_drivers::{
 //!     device::console::VirtIOConsole,
 //!     transport::{mmio::MmioTransport, DeviceType, Transport},
 //! };
 
 //!
+//! # #[cfg(feature = "alloc")]
 //! # fn example<HalImpl: Hal>(transport: MmioTransport) {
 //! if transport.device_type() == DeviceType::Console {
 //!     let mut console = VirtIOConsole::<HalImpl, _>::new(transport).unwrap();