Browse Source

Merge pull request #107 from rcore-os/multiple_blocks

Support reading or writing more than one block at once.
Andrew Walbran 1 year ago
parent
commit
a2d79f1a0a
4 changed files with 54 additions and 45 deletions
  1. 2 2
      examples/aarch64/src/main.rs
  2. 2 2
      examples/riscv/src/main.rs
  3. 2 2
      examples/x86_64/src/main.rs
  4. 48 39
      src/device/blk.rs

+ 2 - 2
examples/aarch64/src/main.rs

@@ -156,8 +156,8 @@ fn virtio_blk<T: Transport>(transport: T) {
         for x in input.iter_mut() {
             *x = i as u8;
         }
-        blk.write_block(i, &input).expect("failed to write");
-        blk.read_block(i, &mut output).expect("failed to read");
+        blk.write_blocks(i, &input).expect("failed to write");
+        blk.read_blocks(i, &mut output).expect("failed to read");
         assert_eq!(input, output);
     }
     info!("virtio-blk test finished");

+ 2 - 2
examples/riscv/src/main.rs

@@ -98,8 +98,8 @@ fn virtio_blk<T: Transport>(transport: T) {
         for x in input.iter_mut() {
             *x = i as u8;
         }
-        blk.write_block(i, &input).expect("failed to write");
-        blk.read_block(i, &mut output).expect("failed to read");
+        blk.write_blocks(i, &input).expect("failed to write");
+        blk.read_blocks(i, &mut output).expect("failed to read");
         assert_eq!(input, output);
     }
     info!("virtio-blk test finished");

+ 2 - 2
examples/x86_64/src/main.rs

@@ -80,8 +80,8 @@ fn virtio_blk<T: Transport>(transport: T) {
         for x in input.iter_mut() {
             *x = i as u8;
         }
-        blk.write_block(i, &input).expect("failed to write");
-        blk.read_block(i, &mut output).expect("failed to read");
+        blk.write_blocks(i, &input).expect("failed to write");
+        blk.read_blocks(i, &mut output).expect("failed to read");
         assert_eq!(input, output);
     }
     info!("virtio-blk test finished");

+ 48 - 39
src/device/blk.rs

@@ -36,8 +36,8 @@ const SUPPORTED_FEATURES: BlkFeature = BlkFeature::RO
 ///
 /// // Read sector 0 and then copy it to sector 1.
 /// let mut buf = [0; SECTOR_SIZE];
-/// disk.read_block(0, &mut buf)?;
-/// disk.write_block(1, &buf)?;
+/// disk.read_blocks(0, &mut buf)?;
+/// disk.write_blocks(1, &buf)?;
 /// # Ok(())
 /// # }
 /// ```
@@ -166,11 +166,14 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         Ok(length)
     }
 
-    /// Reads a block into the given buffer.
+    /// Reads one or more blocks into the given buffer.
+    ///
+    /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
     ///
     /// Blocks until the read completes or there is an error.
-    pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
-        assert_eq!(buf.len(), SECTOR_SIZE);
+    pub fn read_blocks(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
+        assert_ne!(buf.len(), 0);
+        assert_eq!(buf.len() % SECTOR_SIZE, 0);
         self.request_read(
             BlkReq {
                 type_: ReqType::In,
@@ -181,16 +184,16 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         )
     }
 
-    /// Submits a request to read a block, but returns immediately without waiting for the read to
-    /// complete.
+    /// Submits a request to read one or more blocks, but returns immediately without waiting for
+    /// the read to complete.
     ///
     /// # Arguments
     ///
-    /// * `block_id` - The identifier of the block to read.
+    /// * `block_id` - The identifier of the first block to read.
     /// * `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 `complete_read_block`
-    ///   call.
+    ///   contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
+    ///   it needs to be valid (and not otherwise used) until the corresponding
+    ///   `complete_read_blocks` call. Its length must be a non-zero multiple of [`SECTOR_SIZE`].
     /// * `buf` - The buffer in memory into which the block should be read.
     /// * `resp` - A mutable reference to a variable provided by the caller
     ///   to contain the status of the request. The caller can safely
@@ -203,7 +206,7 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
     /// Descriptors to allocate, then it returns [`Error::QueueFull`].
     ///
     /// The caller can then call `peek_used` with the returned token to check whether the device has
-    /// finished handling the request. Once it has, the caller must call `complete_read_block` with
+    /// finished handling the request. Once it has, the caller must call `complete_read_blocks` with
     /// the same buffers before reading the response.
     ///
     /// ```
@@ -216,13 +219,13 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
     /// 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) }?;
+    /// let token = unsafe { blk.read_blocks_nb(42, &mut request, &mut buffer, &mut response) }?;
     ///
     /// // Wait for an interrupt to tell us that the request completed...
     /// assert_eq!(blk.peek_used(), Some(token));
     ///
     /// unsafe {
-    ///   blk.complete_read_block(token, &request, &mut buffer, &mut response)?;
+    ///   blk.complete_read_blocks(token, &request, &mut buffer, &mut response)?;
     /// }
     /// if response.status() == RespStatus::OK {
     ///   println!("Successfully read block.");
@@ -238,14 +241,15 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
     /// `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(
+    pub unsafe fn read_blocks_nb(
         &mut self,
         block_id: usize,
         req: &mut BlkReq,
         buf: &mut [u8],
         resp: &mut BlkResp,
     ) -> Result<u16> {
-        assert_eq!(buf.len(), SECTOR_SIZE);
+        assert_ne!(buf.len(), 0);
+        assert_eq!(buf.len() % SECTOR_SIZE, 0);
         *req = BlkReq {
             type_: ReqType::In,
             reserved: 0,
@@ -260,13 +264,13 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         Ok(token)
     }
 
-    /// Completes a read operation which was started by `read_block_nb`.
+    /// Completes a read operation which was started by `read_blocks_nb`.
     ///
     /// # Safety
     ///
-    /// The same buffers must be passed in again as were passed to `read_block_nb` when it returned
+    /// The same buffers must be passed in again as were passed to `read_blocks_nb` when it returned
     /// the token.
-    pub unsafe fn complete_read_block(
+    pub unsafe fn complete_read_blocks(
         &mut self,
         token: u16,
         req: &BlkReq,
@@ -278,11 +282,14 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         resp.status.into()
     }
 
-    /// Writes the contents of the given buffer to a block.
+    /// Writes the contents of the given buffer to a block or blocks.
+    ///
+    /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
     ///
     /// Blocks until the write is complete or there is an error.
-    pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result {
-        assert_eq!(buf.len(), SECTOR_SIZE);
+    pub fn write_blocks(&mut self, block_id: usize, buf: &[u8]) -> Result {
+        assert_ne!(buf.len(), 0);
+        assert_eq!(buf.len() % SECTOR_SIZE, 0);
         self.request_write(
             BlkReq {
                 type_: ReqType::Out,
@@ -293,36 +300,38 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         )
     }
 
-    /// Submits a request to write a block, but returns immediately without waiting for the write to
-    /// complete.
+    /// Submits a request to write one or more blocks, but returns immediately without waiting for
+    /// the write to complete.
     ///
     /// # Arguments
     ///
-    /// * `block_id` - The identifier of the block to write.
+    /// * `block_id` - The identifier of the first block to write.
     /// * `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 `complete_read_block`
-    ///   call.
-    /// * `buf` - The buffer in memory containing the data to write to the block.
+    ///   contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
+    ///   it needs to be valid (and not otherwise used) until the corresponding
+    ///   `complete_write_blocks` call.
+    /// * `buf` - The buffer in memory containing the data to write to the blocks. Its length must
+    ///   be a non-zero multiple of [`SECTOR_SIZE`].
     /// * `resp` - A mutable reference to a variable provided by the caller
     ///   to contain the status of the request. The caller can safely
     ///   read the variable only after the request is complete.
     ///
     /// # Usage
     ///
-    /// See [VirtIOBlk::read_block_nb].
+    /// See [VirtIOBlk::read_blocks_nb].
     ///
     /// # Safety
     ///
-    /// See  [VirtIOBlk::read_block_nb].
-    pub unsafe fn write_block_nb(
+    /// See  [VirtIOBlk::read_blocks_nb].
+    pub unsafe fn write_blocks_nb(
         &mut self,
         block_id: usize,
         req: &mut BlkReq,
         buf: &[u8],
         resp: &mut BlkResp,
     ) -> Result<u16> {
-        assert_eq!(buf.len(), SECTOR_SIZE);
+        assert_ne!(buf.len(), 0);
+        assert_eq!(buf.len() % SECTOR_SIZE, 0);
         *req = BlkReq {
             type_: ReqType::Out,
             reserved: 0,
@@ -337,13 +346,13 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
         Ok(token)
     }
 
-    /// Completes a write operation which was started by `write_block_nb`.
+    /// Completes a write operation which was started by `write_blocks_nb`.
     ///
     /// # Safety
     ///
-    /// The same buffers must be passed in again as were passed to `write_block_nb` when it returned
-    /// the token.
-    pub unsafe fn complete_write_block(
+    /// The same buffers must be passed in again as were passed to `write_blocks_nb` when it
+    /// returned the token.
+    pub unsafe fn complete_write_blocks(
         &mut self,
         token: u16,
         req: &BlkReq,
@@ -648,7 +657,7 @@ mod tests {
 
         // Read a block from the device.
         let mut buffer = [0; 512];
-        blk.read_block(42, &mut buffer).unwrap();
+        blk.read_blocks(42, &mut buffer).unwrap();
         assert_eq!(&buffer[0..9], b"Test data");
 
         handle.join().unwrap();
@@ -721,7 +730,7 @@ mod tests {
         // Write a block to the device.
         let mut buffer = [0; 512];
         buffer[0..9].copy_from_slice(b"Test data");
-        blk.write_block(42, &mut buffer).unwrap();
+        blk.write_blocks(42, &mut buffer).unwrap();
 
         // Request to flush should be ignored as the device doesn't support it.
         blk.flush().unwrap();