Explorar el Código

IRQ-based VirtIOBlk access. (#6)

* IRQ-based VirtIOBlk accesses.

* Cargo fmt

* Mark nb methods with unsafe and add some safety warnings.

* Make BlkResp and RespStatus public in lib.rs
Yifan Wu hace 3 años
padre
commit
f2fb8b9ded
Se han modificado 3 ficheros con 114 adiciones y 4 borrados
  1. 108 3
      src/blk.rs
  2. 1 1
      src/lib.rs
  3. 5 0
      src/queue.rs

+ 108 - 3
src/blk.rs

@@ -71,6 +71,51 @@ impl VirtIOBlk<'_> {
         }
     }
 
+    /// Read a block in a non-blocking way which means that it returns immediately.
+    ///
+    /// # Arguments
+    ///
+    /// * `block_id` - The identifier of the block to read.
+    /// * `buf` - The buffer in the memory which the block is read into.
+    /// * `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.
+    ///
+    /// # Usage
+    ///
+    /// 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.
+    ///
+    /// # 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.
+    pub unsafe fn read_block_nb(
+        &mut self,
+        block_id: usize,
+        buf: &mut [u8],
+        resp: &mut BlkResp,
+    ) -> Result<u16> {
+        assert_eq!(buf.len(), BLK_SIZE);
+        let req = BlkReq {
+            type_: ReqType::In,
+            reserved: 0,
+            sector: block_id as u64,
+        };
+        let token = self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
+        self.header.notify(0);
+        Ok(token)
+    }
+
     /// Write a block.
     pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result {
         assert_eq!(buf.len(), BLK_SIZE);
@@ -91,6 +136,53 @@ impl VirtIOBlk<'_> {
             _ => Err(Error::IoError),
         }
     }
+
+    //// Write a block in a non-blocking way which means that it returns immediately.
+    ///
+    /// # Arguments
+    ///
+    /// * `block_id` - The identifier of the block to write.
+    /// * `buf` - The buffer in the 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.
+    ///
+    /// # Usage
+    ///
+    /// See also [VirtIOBlk::read_block_nb()].
+    ///
+    /// # Safety
+    ///
+    /// See also [VirtIOBlk::read_block_nb()].
+    pub unsafe fn write_block_nb(
+        &mut self,
+        block_id: usize,
+        buf: &[u8],
+        resp: &mut BlkResp,
+    ) -> Result<u16> {
+        assert_eq!(buf.len(), BLK_SIZE);
+        let req = BlkReq {
+            type_: ReqType::Out,
+            reserved: 0,
+            sector: block_id as u64,
+        };
+        let token = self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
+        self.header.notify(0);
+        Ok(token)
+    }
+
+    /// During an interrupt, it fetches a token of a completed request from the used
+    /// ring and return it. If all completed requests have already been fetched, return
+    /// Err(Error::NotReady).
+    pub fn pop_used(&mut self) -> Result<u16> {
+        self.queue.pop_used().map(|p| p.0)
+    }
+
+    /// Return size of its VirtQueue.
+    /// It can be used to tell the caller how many channels he should monitor on.
+    pub fn virt_queue_size(&self) -> u16 {
+        self.queue.size()
+    }
 }
 
 #[repr(C)]
@@ -119,12 +211,20 @@ struct BlkReq {
     sector: u64,
 }
 
+/// Response of a VirtIOBlk request.
 #[repr(C)]
 #[derive(Debug)]
-struct BlkResp {
+pub struct BlkResp {
     status: RespStatus,
 }
 
+impl BlkResp {
+    /// Return the status of a VirtIOBlk request.
+    pub fn status(&self) -> RespStatus {
+        self.status
+    }
+}
+
 #[repr(u32)]
 #[derive(Debug)]
 enum ReqType {
@@ -135,12 +235,17 @@ enum ReqType {
     WriteZeroes = 13,
 }
 
+/// Status of a VirtIOBlk request.
 #[repr(u8)]
-#[derive(Debug, Eq, PartialEq)]
-enum RespStatus {
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum RespStatus {
+    /// Ok.
     Ok = 0,
+    /// IoErr.
     IoErr = 1,
+    /// Unsupported yet.
     Unsupported = 2,
+    /// Not ready.
     _NotReady = 3,
 }
 

+ 1 - 1
src/lib.rs

@@ -19,7 +19,7 @@ mod input;
 mod net;
 mod queue;
 
-pub use self::blk::VirtIOBlk;
+pub use self::blk::{BlkResp, RespStatus, VirtIOBlk};
 pub use self::console::VirtIOConsole;
 pub use self::gpu::VirtIOGpu;
 pub use self::header::*;

+ 5 - 0
src/queue.rs

@@ -170,6 +170,11 @@ impl VirtQueue<'_> {
 
         Ok((index, len))
     }
+
+    /// Return size of the queue.
+    pub fn size(&self) -> u16 {
+        self.queue_size
+    }
 }
 
 /// The inner layout of a VirtQueue.