summary refs log tree commit diff
path: root/devices/src/virtio/gpu/protocol.rs
diff options
context:
space:
mode:
authorChirantan Ekbote <chirantan@chromium.org>2019-08-16 15:40:48 +0900
committerCommit Bot <commit-bot@chromium.org>2019-10-15 18:26:29 +0000
commitb5964164c4d6187971495ccf82fd64a1d5bde232 (patch)
tree29342794c8f3adbcb8f1d6657cd897aad1d70a59 /devices/src/virtio/gpu/protocol.rs
parent2515b756302235e69fbc1b07ae70270be204a91d (diff)
downloadcrosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.gz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.bz2
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.lz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.xz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.zst
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.zip
devices: Refactor DescriptorChainConsumer, Reader, and Writer
Refactor the Reader and Writer implementations for DescriptorChains.
This has several changes:

  * Change the DescriptorChainConsumer to keep a
    VecDeque<VolatileSlice> instead of an iterator.  This delegates the
    fiddly business of sub-slicing chunks of memory to the VolatileSlice
    implementation.
  * Read in the entire DescriptorChain once when the Reader or Writer is
    first constructed.  This allows us to validate the DescriptorChain
    in the beginning rather than having to deal with an invalid
    DescriptorChain in the middle of the device operating on it.
    Combined with the check that enforces the ordering of read/write
    descriptors in a previous change we can be sure that the entire
    descriptor chain that we have copied in is valid.
  * Add a new `split_at` method so that we can split the Reader/Writer
    into multiple pieces, each responsible for reading/writing a
    separate part of the DescriptorChain.  This is particularly useful
    for implementing zero-copy data transfer as we sometimes need to
    write the data first and then update an earlier part of the buffer
    with the number of bytes written.
  * Stop caching the available bytes in the DescriptorChain.  The
    previous implementation iterated over the remaining descriptors in
    the chain and then only updated the cached value.  If a mis-behaving
    guest then changed one of the later descriptors, the cached value
    would no longer be valid.
  * Check for integer overflow when calculating the number of bytes
    available in the chain.  A guest could fill a chain with five 1GB
    descriptors and cause an integer overflow on a 32-bit machine.
    This would previously crash the device process since we compile with
    integer overflow checks enabled but it would be better to return an
    error instead.
  * Clean up the Read/Write impls.  Having 2 different functions called
    `read`, with different behavior is just confusing.  Consolidate on
    the Read/Write traits from `std::io`.
  * Change the `read_to` and `write_from` functions to be generic over
    types that implement `FileReadWriteVolatile` since we are not
    allowed to assume that it's safe to call read or write on something
    just because it implements `AsRawFd`.  Also add `*at` variants that
    read or write to a particular offset rather than the kernel offset.
  * Change the callback passed to the `consume` function of
    `DescriptorChainConsumer` to take a `&[VolatileSlice]` instead.
    This way we can use the `*vectored` versions of some methods to
    reduce the number of I/O syscalls we need to make.
  * Change the `Result` types that are returned.  Functions that perform
    I/O return an `io::Result`.  Functions that only work on guest
    memory return a `guest_memory::Result`.  This makes it easier to
    inter-operate with the functions from `std::io`.
  * Change some u64/u32 parameters to usize to avoid having to convert
    back and forth between the two in various places.

BUG=b:136128319
TEST=unit tests

Change-Id: I15102f7b4035d66b5ce0891df42b656411e8279f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1757240
Auto-Submit: Chirantan Ekbote <chirantan@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'devices/src/virtio/gpu/protocol.rs')
-rw-r--r--devices/src/virtio/gpu/protocol.rs25
1 files changed, 22 insertions, 3 deletions
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
index c6773dd..dacc850 100644
--- a/devices/src/virtio/gpu/protocol.rs
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -7,6 +7,7 @@
 
 use std::cmp::min;
 use std::fmt::{self, Display};
+use std::io::{self, Write};
 use std::marker::PhantomData;
 use std::mem::{size_of, size_of_val};
 use std::str::from_utf8;
@@ -589,6 +590,8 @@ pub enum GpuCommandDecodeError {
     Memory(DescriptorError),
     /// The type of the command was invalid.
     InvalidType(u32),
+    /// An I/O error occurred.
+    IO(io::Error),
 }
 
 impl Display for GpuCommandDecodeError {
@@ -602,6 +605,7 @@ impl Display for GpuCommandDecodeError {
                 e,
             ),
             InvalidType(n) => write!(f, "invalid command type ({})", n),
+            IO(e) => write!(f, "an I/O error occurred: {}", e),
         }
     }
 }
@@ -612,6 +616,12 @@ impl From<DescriptorError> for GpuCommandDecodeError {
     }
 }
 
+impl From<io::Error> for GpuCommandDecodeError {
+    fn from(e: io::Error) -> GpuCommandDecodeError {
+        GpuCommandDecodeError::IO(e)
+    }
+}
+
 impl fmt::Debug for GpuCommand {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use self::GpuCommand::*;
@@ -754,6 +764,8 @@ pub enum GpuResponseEncodeError {
     TooManyDisplays(usize),
     /// More planes than are valid were in a `OkResourcePlaneInfo`.
     TooManyPlanes(usize),
+    /// An I/O error occurred.
+    IO(io::Error),
 }
 
 impl Display for GpuResponseEncodeError {
@@ -768,6 +780,7 @@ impl Display for GpuResponseEncodeError {
             ),
             TooManyDisplays(n) => write!(f, "{} is more displays than are valid", n),
             TooManyPlanes(n) => write!(f, "{} is more planes than are valid", n),
+            IO(e) => write!(f, "an I/O error occurred: {}", e),
         }
     }
 }
@@ -778,6 +791,12 @@ impl From<DescriptorError> for GpuResponseEncodeError {
     }
 }
 
+impl From<io::Error> for GpuResponseEncodeError {
+    fn from(e: io::Error) -> GpuResponseEncodeError {
+        GpuResponseEncodeError::IO(e)
+    }
+}
+
 impl GpuResponse {
     /// Encodes a this `GpuResponse` into `resp` and the given set of metadata.
     pub fn encode(
@@ -823,7 +842,7 @@ impl GpuResponse {
             }
             GpuResponse::OkCapset(ref data) => {
                 resp.write_obj(hdr)?;
-                resp.write(data)?;
+                resp.write_all(data)?;
                 size_of_val(&hdr) + data.len()
             }
             GpuResponse::OkResourcePlaneInfo {
@@ -847,7 +866,7 @@ impl GpuResponse {
                     strides,
                     offsets,
                 };
-                if resp.available_bytes() >= size_of_val(&plane_info) {
+                if resp.available_bytes()? >= size_of_val(&plane_info) {
                     resp.write_obj(plane_info)?;
                     size_of_val(&plane_info)
                 } else {
@@ -869,7 +888,7 @@ impl GpuResponse {
                 };
 
                 resp.write_obj(resp_info)?;
-                resp.write(&res_info.response)?;
+                resp.write_all(&res_info.response)?;
                 size_of_val(&resp_info) + res_info.response.len()
             }
             _ => {