Procházet zdrojové kódy

Improve non-blocking API of VirtIOBlk.

Pass request buffer into read_block_nb and write_block_nb rather than
allocating it on the stack, so that it can be kept around until the
request completes.

This fixes bug #7.
Andrew Walbran před 2 roky
rodič
revize
206f30ba4f
2 změnil soubory, kde provedl 61 přidání a 24 odebrání
  1. 60 23
      src/blk.rs
  2. 1 1
      src/lib.rs

+ 60 - 23
src/blk.rs

@@ -88,42 +88,63 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         }
     }
 
-    /// Read a block in a non-blocking way which means that it returns immediately.
+    /// Submits a request to read a block, but returns immediately without waiting for the read to
+    /// complete.
     ///
     /// # Arguments
     ///
     /// * `block_id` - The identifier of the block to read.
-    /// * `buf` - The buffer in the memory which the block is read into.
+    /// * `req` - A buffer which the driver can use for the request to send to the device. The
+    ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
+    ///   needs to be valid (and not otherwise used) until the corresponding `pop_used` call.
+    /// * `buf` - The buffer in memory into which the block should be read.
     /// * `resp` - A mutable reference to a variable provided by the caller
-    ///   which contains the status of the requests. The caller can safely
-    ///   read the variable only after the request is ready.
+    ///   to contain the status of the request. The caller can safely
+    ///   read the variable only after the request is complete.
     ///
     /// # Usage
     ///
-    /// It will submit request to the virtio block device and return a token identifying
+    /// It will submit request to the VirtIO block device and return a token identifying
     /// the position of the first Descriptor in the chain. If there are not enough
     /// Descriptors to allocate, then it returns [Error::BufferTooSmall].
     ///
-    /// After the request is ready, `resp` will be updated and the caller can get the
-    /// status of the request(e.g. succeed or failed) through it. However, the caller
-    /// **must not** spin on `resp` to wait for it to change. A safe way is to read it
-    /// after the same token as this method returns is fetched through [VirtIOBlk::pop_used()],
-    /// which means that the request has been ready.
+    /// The caller can then call `pop_used` to check whether the device has finished handling the
+    /// request. Once it has, the caller can then read the response and dispose of the buffers.
+    ///
+    /// ```
+    /// # use virtio_drivers::{BlkReq, BlkResp, Error, Hal, RespStatus, Transport, VirtIOBlk};
+    /// # fn example<H: Hal, T: Transport>(blk: &mut VirtIOBlk<H, T>) -> Result<(), Error> {
+    /// let mut request = BlkReq::default();
+    /// let mut buffer = [0; 512];
+    /// let mut response = BlkResp::default();
+    /// let token = unsafe { blk.read_block_nb(42, &mut request, &mut buffer, &mut response) }?;
+    ///
+    /// // Wait for an interrupt to tell us that the request completed...
+    ///
+    /// assert_eq!(blk.pop_used()?, token);
+    /// if response.status() == RespStatus::OK {
+    ///   println!("Successfully read block.");
+    /// } else {
+    ///   println!("Error {:?} reading block.", response.status());
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
     ///
     /// # Safety
     ///
-    /// `buf` is still borrowed by the underlying virtio block device even if this
-    /// method returns. Thus, it is the caller's responsibility to guarantee that
-    /// `buf` is not accessed before the request is completed in order to avoid
-    /// data races.
+    /// `req`, `buf` and `resp` are still borrowed by the underlying VirtIO block device even after
+    /// this method returns. Thus, it is the caller's responsibility to guarantee that they are not
+    /// accessed before the request is completed in order to avoid data races.
     pub unsafe fn read_block_nb(
         &mut self,
         block_id: usize,
+        req: &mut BlkReq,
         buf: &mut [u8],
         resp: &mut BlkResp,
     ) -> Result<u16> {
         assert_eq!(buf.len(), SECTOR_SIZE);
-        let req = BlkReq {
+        *req = BlkReq {
             type_: ReqType::In,
             reserved: 0,
             sector: block_id as u64,
@@ -155,31 +176,36 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         }
     }
 
-    //// Write a block in a non-blocking way which means that it returns immediately.
+    /// Submits a request to write a block, but returns immediately without waiting for the write to
+    /// complete.
     ///
     /// # Arguments
     ///
     /// * `block_id` - The identifier of the block to write.
-    /// * `buf` - The buffer in the memory containing the data to write to the block.
+    /// * `req` - A buffer which the driver can use for the request to send to the device. The
+    ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
+    ///   needs to be valid (and not otherwise used) until the corresponding `pop_used` call.
+    /// * `buf` - The buffer in memory containing the data to write to the block.
     /// * `resp` - A mutable reference to a variable provided by the caller
-    ///   which contains the status of the requests. The caller can safely
-    ///   read the variable only after the request is ready.
+    ///   to contain the status of the request. The caller can safely
+    ///   read the variable only after the request is complete.
     ///
     /// # Usage
     ///
-    /// See also [VirtIOBlk::read_block_nb()].
+    /// See [VirtIOBlk::read_block_nb].
     ///
     /// # Safety
     ///
-    /// See also [VirtIOBlk::read_block_nb()].
+    /// See  [VirtIOBlk::read_block_nb].
     pub unsafe fn write_block_nb(
         &mut self,
         block_id: usize,
+        req: &mut BlkReq,
         buf: &[u8],
         resp: &mut BlkResp,
     ) -> Result<u16> {
         assert_eq!(buf.len(), SECTOR_SIZE);
-        let req = BlkReq {
+        *req = BlkReq {
             type_: ReqType::Out,
             reserved: 0,
             sector: block_id as u64,
@@ -231,14 +257,25 @@ struct BlkConfig {
     // ... ignored
 }
 
+/// A VirtIO block device request.
 #[repr(C)]
 #[derive(AsBytes, Debug)]
-struct BlkReq {
+pub struct BlkReq {
     type_: ReqType,
     reserved: u32,
     sector: u64,
 }
 
+impl Default for BlkReq {
+    fn default() -> Self {
+        Self {
+            type_: ReqType::In,
+            reserved: 0,
+            sector: 0,
+        }
+    }
+}
+
 /// Response of a VirtIOBlk request.
 #[repr(C)]
 #[derive(AsBytes, Debug, FromBytes)]

+ 1 - 1
src/lib.rs

@@ -19,7 +19,7 @@ mod queue;
 mod transport;
 mod volatile;
 
-pub use self::blk::{BlkResp, RespStatus, VirtIOBlk};
+pub use self::blk::{BlkReq, BlkResp, RespStatus, VirtIOBlk};
 pub use self::console::VirtIOConsole;
 pub use self::gpu::VirtIOGpu;
 pub use self::hal::{Hal, PhysAddr, VirtAddr};