summary refs log tree commit diff
diff options
context:
space:
mode:
authorJingkui Wang <jkwang@google.com>2018-10-03 13:04:47 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-11-19 16:28:35 -0800
commite13b180f74ad6d0b1cb1db2a198e294da97e526c (patch)
tree438e0623a7ec0b0583087da9a5214e6008fad2d9
parentb23c091c8bfd37cab8be15c0912d684187969c63 (diff)
downloadcrosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar.gz
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar.bz2
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar.lz
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar.xz
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.tar.zst
crosvm-e13b180f74ad6d0b1cb1db2a198e294da97e526c.zip
crosvm: use msg_socket in vm_control
Refactor existing code to use msg_socket.

BUG=None
TEST=local build and run

Change-Id: Iee72326b330e035303f679e1aedd6e5d18ad4f8a
Reviewed-on: https://chromium-review.googlesource.com/1260260
Commit-Ready: Jingkui Wang <jkwang@google.com>
Tested-by: Jingkui Wang <jkwang@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--Cargo.lock3
-rw-r--r--devices/src/virtio/wl.rs15
-rw-r--r--kvm/Cargo.toml1
-rw-r--r--kvm/src/lib.rs4
-rw-r--r--resources/Cargo.toml1
-rw-r--r--resources/src/gpu_allocator.rs5
-rw-r--r--resources/src/lib.rs2
-rw-r--r--src/linux.rs17
-rw-r--r--src/main.rs8
-rw-r--r--vm_control/Cargo.toml1
-rw-r--r--vm_control/src/lib.rs530
11 files changed, 67 insertions, 520 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d6f8784..0bc80ab 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -219,6 +219,7 @@ version = "0.1.0"
 dependencies = [
  "kvm_sys 0.1.0",
  "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
  "sys_util 0.1.0",
 ]
 
@@ -373,6 +374,7 @@ dependencies = [
  "data_model 0.1.0",
  "gpu_buffer 0.1.0",
  "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
  "sys_util 0.1.0",
 ]
 
@@ -438,6 +440,7 @@ dependencies = [
  "data_model 0.1.0",
  "kvm 0.1.0",
  "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
  "resources 0.1.0",
  "sys_util 0.1.0",
 ]
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index 0b973bd..7b9ffc8 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -56,6 +56,7 @@ use libc::{dup, EBADF, EINVAL};
 use data_model::VolatileMemoryError;
 use data_model::*;
 
+use msg_socket::{MsgError, MsgReceiver, MsgSender, MsgSocket};
 use resources::GpuMemoryDesc;
 use sys_util::{
     pipe, round_up_to_page_size, Error, EventFd, FileFlags, GuestAddress, GuestMemory,
@@ -66,7 +67,7 @@ use sys_util::{
 use sys_util::ioctl_with_ref;
 
 use super::{DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_WL};
-use vm_control::{MaybeOwnedFd, VmControlError, VmRequest, VmResponse};
+use vm_control::{MaybeOwnedFd, VmRequest, VmResponse};
 
 const VIRTWL_SEND_MAX_ALLOCS: usize = 28;
 const VIRTIO_WL_CMD_VFD_NEW: u32 = 256;
@@ -409,7 +410,7 @@ enum WlError {
     AllocSetSize(Error),
     SocketConnect(io::Error),
     SocketNonBlock(io::Error),
-    VmControl(VmControlError),
+    VmControl(MsgError),
     VmBadResponse,
     CheckedOffset,
     GuestMemory(GuestMemoryError),
@@ -467,21 +468,23 @@ impl From<VolatileMemoryError> for WlError {
 
 #[derive(Clone)]
 struct VmRequester {
-    inner: Rc<RefCell<UnixDatagram>>,
+    inner: Rc<RefCell<MsgSocket<VmRequest, VmResponse>>>,
 }
 
 impl VmRequester {
     fn new(vm_socket: UnixDatagram) -> VmRequester {
         VmRequester {
-            inner: Rc::new(RefCell::new(vm_socket)),
+            inner: Rc::new(RefCell::new(MsgSocket::<VmRequest, VmResponse>::new(
+                vm_socket,
+            ))),
         }
     }
 
     fn request(&self, request: VmRequest) -> WlResult<VmResponse> {
         let mut inner = self.inner.borrow_mut();
         let ref mut vm_socket = *inner;
-        request.send(vm_socket).map_err(WlError::VmControl)?;
-        VmResponse::recv(vm_socket).map_err(WlError::VmControl)
+        vm_socket.send(&request).map_err(WlError::VmControl)?;
+        vm_socket.recv().map_err(WlError::VmControl)
     }
 }
 
diff --git a/kvm/Cargo.toml b/kvm/Cargo.toml
index 11afad4..2281598 100644
--- a/kvm/Cargo.toml
+++ b/kvm/Cargo.toml
@@ -7,3 +7,4 @@ authors = ["The Chromium OS Authors"]
 libc = "*"
 kvm_sys = { path = "../kvm_sys" }
 sys_util = { path = "../sys_util" }
+msg_socket = { path = "../msg_socket" }
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 050e1cd..72bea5e 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -8,6 +8,7 @@ extern crate kvm_sys;
 extern crate libc;
 #[macro_use]
 extern crate sys_util;
+extern crate msg_socket;
 
 mod cap;
 
@@ -25,6 +26,7 @@ use libc::{open, EINVAL, ENOENT, ENOSPC, O_CLOEXEC, O_RDWR};
 
 use kvm_sys::*;
 
+use msg_socket::MsgOnSocket;
 #[allow(unused_imports)]
 use sys_util::{
     ioctl, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val,
@@ -239,7 +241,7 @@ impl AsRawFd for Kvm {
 }
 
 /// An address either in programmable I/O space or in memory mapped I/O space.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, MsgOnSocket)]
 pub enum IoeventAddress {
     Pio(u64),
     Mmio(u64),
diff --git a/resources/Cargo.toml b/resources/Cargo.toml
index 29fc38a..c563d20 100644
--- a/resources/Cargo.toml
+++ b/resources/Cargo.toml
@@ -11,3 +11,4 @@ data_model = { path = "../data_model" }
 gpu_buffer = { path = "../gpu_buffer", optional = true }
 libc = "*"
 sys_util = { path = "../sys_util" }
+msg_socket = { path = "../msg_socket" }
diff --git a/resources/src/gpu_allocator.rs b/resources/src/gpu_allocator.rs
index 9f98bcf..16dc944 100644
--- a/resources/src/gpu_allocator.rs
+++ b/resources/src/gpu_allocator.rs
@@ -9,6 +9,7 @@ use libc::EINVAL;
 
 #[cfg(feature = "wl-dmabuf")]
 use gpu_buffer;
+use msg_socket::MsgOnSocket;
 use sys_util;
 
 #[derive(Debug)]
@@ -18,14 +19,14 @@ pub enum GpuAllocatorError {
 }
 
 /// Struct that describes the offset and stride of a plane located in GPU memory.
-#[derive(Clone, Copy, Debug, PartialEq, Default)]
+#[derive(Clone, Copy, Debug, PartialEq, Default, MsgOnSocket)]
 pub struct GpuMemoryPlaneDesc {
     pub stride: u32,
     pub offset: u32,
 }
 
 /// Struct that describes a GPU memory allocation that consists of up to 3 planes.
-#[derive(Clone, Copy, Debug, Default)]
+#[derive(Clone, Copy, Debug, Default, MsgOnSocket)]
 pub struct GpuMemoryDesc {
     pub planes: [GpuMemoryPlaneDesc; 3],
 }
diff --git a/resources/src/lib.rs b/resources/src/lib.rs
index 5d12cfc..e19ac7c 100644
--- a/resources/src/lib.rs
+++ b/resources/src/lib.rs
@@ -8,6 +8,8 @@
 extern crate gpu_buffer;
 extern crate libc;
 extern crate sys_util;
+#[macro_use]
+extern crate msg_socket;
 
 mod address_allocator;
 mod gpu_allocator;
diff --git a/src/linux.rs b/src/linux.rs
index 40f32a1..44c2aef 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -28,12 +28,13 @@ use byteorder::{ByteOrder, LittleEndian};
 use devices::{self, PciDevice, VirtioPciDevice};
 use io_jail::{self, Minijail};
 use kvm::*;
+use msg_socket::{MsgReceiver, MsgSender, UnlinkMsgSocket};
 use net_util::Tap;
 use qcow::{self, ImageType, QcowFile};
 use sys_util;
 use sys_util::*;
 use vhost;
-use vm_control::VmRequest;
+use vm_control::{VmRequest, VmResponse};
 
 use Config;
 
@@ -788,11 +789,15 @@ pub fn run_config(cfg: Config) -> Result<()> {
     if let Some(ref path_string) = cfg.socket_path {
         let path = Path::new(path_string);
         let dgram = UnixDatagram::bind(path).map_err(Error::CreateSocket)?;
-        control_sockets.push(UnlinkUnixDatagram(dgram));
+        control_sockets.push(UnlinkMsgSocket::<VmResponse, VmRequest>::new(
+            UnlinkUnixDatagram(dgram),
+        ));
     };
     let (wayland_host_socket, wayland_device_socket) =
         UnixDatagram::pair().map_err(Error::CreateSocket)?;
-    control_sockets.push(UnlinkUnixDatagram(wayland_host_socket));
+    control_sockets.push(UnlinkMsgSocket::<VmResponse, VmRequest>::new(
+        UnlinkUnixDatagram(wayland_host_socket),
+    ));
     // Balloon gets a special socket so balloon requests can be forwarded from the main process.
     let (balloon_host_socket, balloon_device_socket) =
         UnixDatagram::pair().map_err(Error::CreateSocket)?;
@@ -805,7 +810,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
 
 fn run_control(
     mut linux: RunnableLinuxVm,
-    control_sockets: Vec<UnlinkUnixDatagram>,
+    control_sockets: Vec<UnlinkMsgSocket<VmResponse, VmRequest>>,
     balloon_host_socket: UnixDatagram,
     sigchld_fd: SignalFd,
 ) -> Result<()> {
@@ -1045,7 +1050,7 @@ fn run_control(
                 }
                 Token::VmControl { index } => {
                     if let Some(socket) = control_sockets.get(index as usize) {
-                        match VmRequest::recv(socket.as_ref()) {
+                        match socket.recv() {
                             Ok(request) => {
                                 let mut running = true;
                                 let response = request.execute(
@@ -1054,7 +1059,7 @@ fn run_control(
                                     &mut running,
                                     &balloon_host_socket,
                                 );
-                                if let Err(e) = response.send(socket.as_ref()) {
+                                if let Err(e) = socket.send(&response) {
                                     error!("failed to send VmResponse: {:?}", e);
                                 }
                                 if !running {
diff --git a/src/main.rs b/src/main.rs
index 7158f67..75ad172 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,6 +24,7 @@ extern crate sys_util;
 extern crate data_model;
 #[cfg(feature = "wl-dmabuf")]
 extern crate gpu_buffer;
+extern crate msg_socket;
 #[cfg(feature = "plugin")]
 extern crate plugin_proto;
 #[cfg(feature = "plugin")]
@@ -51,6 +52,7 @@ use qcow::QcowFile;
 use sys_util::{getpid, kill_process_group, reap_child, syslog};
 
 use argument::{print_help, set_arguments, Argument};
+use msg_socket::{MsgSender, Sender};
 use vm_control::VmRequest;
 
 static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm";
@@ -549,7 +551,8 @@ fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
             Ok(s)
         }) {
             Ok(s) => {
-                if let Err(e) = VmRequest::Exit.send(&s) {
+                let sender = Sender::<VmRequest>::new(s);
+                if let Err(e) = sender.send(&VmRequest::Exit) {
                     error!(
                         "failed to send stop request to socket at '{}': {:?}",
                         socket_path, e
@@ -586,7 +589,8 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
             Ok(s)
         }) {
             Ok(s) => {
-                if let Err(e) = VmRequest::BalloonAdjust(num_pages).send(&s) {
+                let sender = Sender::<VmRequest>::new(s);
+                if let Err(e) = sender.send(&VmRequest::BalloonAdjust(num_pages)) {
                     error!(
                         "failed to send balloon request to socket at '{}': {:?}",
                         socket_path, e
diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml
index c2f6e40..5e03b9e 100644
--- a/vm_control/Cargo.toml
+++ b/vm_control/Cargo.toml
@@ -10,3 +10,4 @@ kvm = { path = "../kvm" }
 libc = "*"
 sys_util = { path = "../sys_util" }
 resources = { path = "../resources" }
+msg_socket = { path = "../msg_socket" }
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 4d1d036..deec07d 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -11,45 +11,24 @@
 //! if the request type expects one.
 
 extern crate byteorder;
-extern crate data_model;
 extern crate kvm;
 extern crate libc;
+extern crate msg_socket;
 extern crate resources;
 extern crate sys_util;
 
 use std::fs::File;
 use std::io::{Seek, SeekFrom};
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::os::unix::net::UnixDatagram;
-use std::result;
 
-use libc::{EINVAL, ENODEV, ERANGE};
+use libc::{EINVAL, ENODEV};
 
 use byteorder::{LittleEndian, WriteBytesExt};
-use data_model::{DataInit, Le32, Le64, VolatileMemory};
 use kvm::{Datamatch, IoeventAddress, Vm};
-use resources::{GpuMemoryDesc, GpuMemoryPlaneDesc, SystemAllocator};
-use sys_util::{
-    Error as SysError, EventFd, GuestAddress, MemoryMapping, MmapError, Result, ScmSocket,
-};
-
-#[derive(Debug, PartialEq)]
-/// An error during a request or response transaction.
-pub enum VmControlError {
-    /// Error while sending a request or response.
-    Send(SysError),
-    /// Error while receiving a request or response.
-    Recv(SysError),
-    /// The type of a received request or response is unknown.
-    InvalidType,
-    /// There was not the expected amount of data when receiving a request or response. The inner
-    /// value is how much data was read.
-    BadSize(usize),
-    /// There was no associated file descriptor received for a request that expected it.
-    ExpectFd,
-}
-
-pub type VmControlResult<T> = result::Result<T, VmControlError>;
+use msg_socket::{MsgOnSocket, MsgResult};
+use resources::{GpuMemoryDesc, SystemAllocator};
+use sys_util::{Error as SysError, EventFd, GuestAddress, MemoryMapping, MmapError, Result};
 
 /// A file descriptor either borrowed or owned by this.
 pub enum MaybeOwnedFd {
@@ -68,9 +47,29 @@ impl AsRawFd for MaybeOwnedFd {
     }
 }
 
+// When sent, it could be owned or borrowed. On the receiver end, it always owned.
+impl MsgOnSocket for MaybeOwnedFd {
+    fn msg_size() -> usize {
+        0usize
+    }
+    fn max_fd_count() -> usize {
+        1usize
+    }
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+        let (fd, size) = RawFd::read_from_buffer(buffer, fds)?;
+        let file = File::from_raw_fd(fd);
+        Ok((MaybeOwnedFd::Owned(file), size))
+    }
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+        let fd = self.as_raw_fd();
+        fd.write_to_buffer(buffer, fds)
+    }
+}
+
 /// A request to the main process to perform some operation on the VM.
 ///
 /// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success.
+#[derive(MsgOnSocket)]
 pub enum VmRequest {
     /// Try to grow or shrink the VM's balloon.
     BalloonAdjust(i32),
@@ -94,28 +93,6 @@ pub enum VmRequest {
     },
 }
 
-const VM_REQUEST_TYPE_EXIT: u32 = 1;
-const VM_REQUEST_TYPE_REGISTER_MEMORY: u32 = 2;
-const VM_REQUEST_TYPE_UNREGISTER_MEMORY: u32 = 3;
-const VM_REQUEST_TYPE_BALLOON_ADJUST: u32 = 4;
-const VM_REQUEST_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY: u32 = 5;
-const VM_REQUEST_SIZE: usize = 32;
-
-#[repr(C)]
-#[derive(Clone, Copy, Default)]
-struct VmRequestStruct {
-    type_: Le32,
-    slot: Le32,
-    size: Le64,
-    num_pages: Le32,
-    width: Le32,
-    height: Le32,
-    format: Le32,
-}
-
-// Safe because it only has data and has no implicit padding.
-unsafe impl DataInit for VmRequestStruct {}
-
 fn register_memory(
     vm: &mut Vm,
     allocator: &mut SystemAllocator,
@@ -140,90 +117,6 @@ fn register_memory(
 }
 
 impl VmRequest {
-    /// Receive a `VmRequest` from the given socket.
-    ///
-    /// A `VmResponse` should be sent out over the given socket before another request is received.
-    pub fn recv(s: &UnixDatagram) -> VmControlResult<VmRequest> {
-        assert_eq!(VM_REQUEST_SIZE, std::mem::size_of::<VmRequestStruct>());
-        let mut buf = [0; VM_REQUEST_SIZE];
-        let (read, file) = s
-            .recv_with_fd(&mut buf)
-            .map_err(|e| VmControlError::Recv(e))?;
-        if read != VM_REQUEST_SIZE {
-            return Err(VmControlError::BadSize(read));
-        }
-        // The unwrap() will never fail because it's referencing a buf statically sized to be large
-        // enough for a VmRequestStruct.
-        let req: VmRequestStruct = buf.as_mut().get_ref(0).unwrap().load();
-
-        match req.type_.into() {
-            VM_REQUEST_TYPE_EXIT => Ok(VmRequest::Exit),
-            VM_REQUEST_TYPE_REGISTER_MEMORY => {
-                let fd = file.ok_or(VmControlError::ExpectFd)?;
-                Ok(VmRequest::RegisterMemory(
-                    MaybeOwnedFd::Owned(fd),
-                    req.size.to_native() as usize,
-                ))
-            }
-            VM_REQUEST_TYPE_UNREGISTER_MEMORY => Ok(VmRequest::UnregisterMemory(req.slot.into())),
-            VM_REQUEST_TYPE_BALLOON_ADJUST => {
-                Ok(VmRequest::BalloonAdjust(req.num_pages.to_native() as i32))
-            }
-            VM_REQUEST_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY => {
-                Ok(VmRequest::AllocateAndRegisterGpuMemory {
-                    width: req.width.to_native(),
-                    height: req.height.to_native(),
-                    format: req.format.to_native(),
-                })
-            }
-            _ => Err(VmControlError::InvalidType),
-        }
-    }
-
-    /// Send a `VmRequest` over the given socket.
-    ///
-    /// After this request is a sent, a `VmResponse` should be received before sending another
-    /// request.
-    pub fn send(&self, s: &UnixDatagram) -> VmControlResult<()> {
-        assert_eq!(VM_REQUEST_SIZE, std::mem::size_of::<VmRequestStruct>());
-        let mut req = VmRequestStruct::default();
-        let mut fd_buf = [0; 1];
-        let mut fd_len = 0;
-        match self {
-            &VmRequest::Exit => req.type_ = Le32::from(VM_REQUEST_TYPE_EXIT),
-            &VmRequest::RegisterMemory(ref fd, size) => {
-                req.type_ = Le32::from(VM_REQUEST_TYPE_REGISTER_MEMORY);
-                req.size = Le64::from(size as u64);
-                fd_buf[0] = fd.as_raw_fd();
-                fd_len = 1;
-            }
-            &VmRequest::UnregisterMemory(slot) => {
-                req.type_ = Le32::from(VM_REQUEST_TYPE_UNREGISTER_MEMORY);
-                req.slot = Le32::from(slot);
-            }
-            &VmRequest::BalloonAdjust(pages) => {
-                req.type_ = Le32::from(VM_REQUEST_TYPE_BALLOON_ADJUST);
-                req.num_pages = Le32::from(pages as u32);
-            }
-            &VmRequest::AllocateAndRegisterGpuMemory {
-                width,
-                height,
-                format,
-            } => {
-                req.type_ = Le32::from(VM_REQUEST_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY);
-                req.width = Le32::from(width as u32);
-                req.height = Le32::from(height as u32);
-                req.format = Le32::from(format as u32);
-            }
-            _ => return Err(VmControlError::InvalidType),
-        }
-        let mut buf = [0; VM_REQUEST_SIZE];
-        buf.as_mut().get_ref(0).unwrap().store(req);
-        s.send_with_fds(buf.as_ref(), &fd_buf[..fd_len])
-            .map_err(|e| VmControlError::Send(e))?;
-        Ok(())
-    }
-
     /// Executes this request on the given Vm and other mutable state.
     ///
     /// # Arguments
@@ -313,6 +206,7 @@ impl VmRequest {
 /// Indication of success or failure of a `VmRequest`.
 ///
 /// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response.
+#[derive(MsgOnSocket)]
 pub enum VmResponse {
     /// Indicates the request was executed successfully.
     Ok,
@@ -330,373 +224,3 @@ pub enum VmResponse {
         desc: GpuMemoryDesc,
     },
 }
-
-const VM_RESPONSE_TYPE_OK: u32 = 1;
-const VM_RESPONSE_TYPE_ERR: u32 = 2;
-const VM_RESPONSE_TYPE_REGISTER_MEMORY: u32 = 3;
-const VM_RESPONSE_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY: u32 = 4;
-const VM_RESPONSE_SIZE: usize = 48;
-
-#[repr(C)]
-#[derive(Clone, Copy, Default)]
-struct VmResponseStruct {
-    type_: Le32,
-    errno: Le32,
-    pfn: Le64,
-    slot: Le32,
-    stride0: Le32,
-    stride1: Le32,
-    stride2: Le32,
-    offset0: Le32,
-    offset1: Le32,
-    offset2: Le32,
-}
-
-// Safe because it only has data and has no implicit padding.
-unsafe impl DataInit for VmResponseStruct {}
-
-impl VmResponse {
-    /// Receive a `VmResponse` from the given socket.
-    ///
-    /// This should be called after the sending a `VmRequest` before sending another request.
-    pub fn recv(s: &UnixDatagram) -> VmControlResult<VmResponse> {
-        let mut buf = [0; VM_RESPONSE_SIZE];
-        let (read, file) = s
-            .recv_with_fd(&mut buf)
-            .map_err(|e| VmControlError::Recv(e))?;
-        if read != VM_RESPONSE_SIZE {
-            return Err(VmControlError::BadSize(read));
-        }
-        let resp: VmResponseStruct = buf.as_mut().get_ref(0).unwrap().load();
-
-        match resp.type_.into() {
-            VM_RESPONSE_TYPE_OK => Ok(VmResponse::Ok),
-            VM_RESPONSE_TYPE_ERR => Ok(VmResponse::Err(SysError::new(
-                resp.errno.to_native() as i32
-            ))),
-            VM_RESPONSE_TYPE_REGISTER_MEMORY => Ok(VmResponse::RegisterMemory {
-                pfn: resp.pfn.into(),
-                slot: resp.slot.into(),
-            }),
-            VM_RESPONSE_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY => {
-                let fd = file.ok_or(VmControlError::ExpectFd)?;
-                Ok(VmResponse::AllocateAndRegisterGpuMemory {
-                    fd: MaybeOwnedFd::Owned(fd),
-                    pfn: resp.pfn.into(),
-                    slot: resp.slot.into(),
-                    desc: GpuMemoryDesc {
-                        planes: [
-                            GpuMemoryPlaneDesc {
-                                stride: resp.stride0.into(),
-                                offset: resp.offset0.into(),
-                            },
-                            GpuMemoryPlaneDesc {
-                                stride: resp.stride1.into(),
-                                offset: resp.offset1.into(),
-                            },
-                            GpuMemoryPlaneDesc {
-                                stride: resp.stride2.into(),
-                                offset: resp.offset2.into(),
-                            },
-                        ],
-                    },
-                })
-            }
-            _ => Err(VmControlError::InvalidType),
-        }
-    }
-
-    /// Send a `VmResponse` over the given socket.
-    ///
-    /// This must be called after receiving a `VmRequest` to indicate the outcome of that request's
-    /// execution.
-    pub fn send(&self, s: &UnixDatagram) -> VmControlResult<()> {
-        let mut resp = VmResponseStruct::default();
-        let mut fd_buf = [0; 1];
-        let mut fd_len = 0;
-        match self {
-            &VmResponse::Ok => resp.type_ = Le32::from(VM_RESPONSE_TYPE_OK),
-            &VmResponse::Err(e) => {
-                resp.type_ = Le32::from(VM_RESPONSE_TYPE_ERR);
-                resp.errno = Le32::from(e.errno().checked_abs().unwrap_or(ERANGE) as u32);
-            }
-            &VmResponse::RegisterMemory { pfn, slot } => {
-                resp.type_ = Le32::from(VM_RESPONSE_TYPE_REGISTER_MEMORY);
-                resp.pfn = Le64::from(pfn);
-                resp.slot = Le32::from(slot);
-            }
-            &VmResponse::AllocateAndRegisterGpuMemory {
-                ref fd,
-                pfn,
-                slot,
-                desc,
-            } => {
-                fd_buf[0] = fd.as_raw_fd();
-                fd_len = 1;
-                resp.type_ = Le32::from(VM_RESPONSE_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY);
-                resp.pfn = Le64::from(pfn);
-                resp.slot = Le32::from(slot);
-                resp.stride0 = Le32::from(desc.planes[0].stride);
-                resp.stride1 = Le32::from(desc.planes[1].stride);
-                resp.stride2 = Le32::from(desc.planes[2].stride);
-                resp.offset0 = Le32::from(desc.planes[0].offset);
-                resp.offset1 = Le32::from(desc.planes[1].offset);
-                resp.offset2 = Le32::from(desc.planes[2].offset);
-            }
-        }
-        let mut buf = [0; VM_RESPONSE_SIZE];
-        buf.as_mut().get_ref(0).unwrap().store(resp);
-        s.send_with_fds(buf.as_ref(), &fd_buf[..fd_len])
-            .map_err(|e| VmControlError::Send(e))?;
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use std::net::Shutdown;
-
-    use sys_util::kernel_has_memfd;
-    use sys_util::SharedMemory;
-
-    #[test]
-    fn request_exit() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        VmRequest::Exit.send(&s1).unwrap();
-        match VmRequest::recv(&s2).unwrap() {
-            VmRequest::Exit => {}
-            _ => panic!("recv wrong request variant"),
-        }
-    }
-
-    #[test]
-    fn request_register_memory() {
-        if !kernel_has_memfd() {
-            return;
-        }
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let shm_size: usize = 4096;
-        let mut shm = SharedMemory::new(None).unwrap();
-        shm.set_size(shm_size as u64).unwrap();
-        VmRequest::RegisterMemory(MaybeOwnedFd::Borrowed(shm.as_raw_fd()), shm_size)
-            .send(&s1)
-            .unwrap();
-        match VmRequest::recv(&s2).unwrap() {
-            VmRequest::RegisterMemory(MaybeOwnedFd::Owned(fd), size) => {
-                assert!(fd.as_raw_fd() >= 0);
-                assert_eq!(size, shm_size);
-            }
-            _ => panic!("recv wrong request variant"),
-        }
-    }
-
-    #[test]
-    fn request_unregister_memory() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        VmRequest::UnregisterMemory(77).send(&s1).unwrap();
-        match VmRequest::recv(&s2).unwrap() {
-            VmRequest::UnregisterMemory(slot) => assert_eq!(slot, 77),
-            _ => panic!("recv wrong request variant"),
-        }
-    }
-
-    #[test]
-    fn request_expect_fd() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let mut bad_request = [0; VM_REQUEST_SIZE];
-        bad_request[0] = VM_REQUEST_TYPE_REGISTER_MEMORY as u8;
-        s2.send_with_fds(bad_request.as_ref(), &[]).unwrap();
-        match VmRequest::recv(&s1) {
-            Err(VmControlError::ExpectFd) => {}
-            _ => panic!("recv wrong error variant"),
-        }
-    }
-
-    #[test]
-    fn request_no_data() {
-        let (s1, _) = UnixDatagram::pair().expect("failed to create socket pair");
-        s1.shutdown(Shutdown::Both).unwrap();
-        match VmRequest::recv(&s1) {
-            Err(VmControlError::BadSize(s)) => assert_eq!(s, 0),
-            _ => panic!("recv wrong error variant"),
-        }
-    }
-
-    #[test]
-    fn request_bad_size() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        s2.send_with_fds([12; 7].as_ref(), &[]).unwrap();
-        match VmRequest::recv(&s1) {
-            Err(VmControlError::BadSize(_)) => {}
-            _ => panic!("recv wrong error variant"),
-        }
-    }
-
-    #[test]
-    fn request_invalid_type() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        s2.send_with_fds([12; VM_REQUEST_SIZE].as_ref(), &[])
-            .unwrap();
-        match VmRequest::recv(&s1) {
-            Err(VmControlError::InvalidType) => {}
-            _ => panic!("recv wrong error variant"),
-        }
-    }
-
-    #[test]
-    fn request_allocate_and_register_gpu_memory() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let gpu_width: u32 = 32;
-        let gpu_height: u32 = 32;
-        let gpu_format: u32 = 0x34325258;
-        let r = VmRequest::AllocateAndRegisterGpuMemory {
-            width: gpu_width,
-            height: gpu_height,
-            format: gpu_format,
-        };
-        r.send(&s1).unwrap();
-        match VmRequest::recv(&s2).unwrap() {
-            VmRequest::AllocateAndRegisterGpuMemory {
-                width,
-                height,
-                format,
-            } => {
-                assert_eq!(width, gpu_width);
-                assert_eq!(height, gpu_width);
-                assert_eq!(format, gpu_format);
-            }
-            _ => panic!("recv wrong request variant"),
-        }
-    }
-
-    #[test]
-    fn resp_ok() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        VmResponse::Ok.send(&s1).unwrap();
-        match VmResponse::recv(&s2).unwrap() {
-            VmResponse::Ok => {}
-            _ => panic!("recv wrong response variant"),
-        }
-    }
-
-    #[test]
-    fn resp_err() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let r1 = VmResponse::Err(SysError::new(libc::EDESTADDRREQ));
-        r1.send(&s1).unwrap();
-        match VmResponse::recv(&s2).unwrap() {
-            VmResponse::Err(e) => {
-                assert_eq!(e, SysError::new(libc::EDESTADDRREQ));
-            }
-            _ => panic!("recv wrong response variant"),
-        }
-    }
-
-    #[test]
-    fn resp_memory() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let memory_pfn = 55;
-        let memory_slot = 66;
-        let r1 = VmResponse::RegisterMemory {
-            pfn: memory_pfn,
-            slot: memory_slot,
-        };
-        r1.send(&s1).unwrap();
-        match VmResponse::recv(&s2).unwrap() {
-            VmResponse::RegisterMemory { pfn, slot } => {
-                assert_eq!(pfn, memory_pfn);
-                assert_eq!(slot, memory_slot);
-            }
-            _ => panic!("recv wrong response variant"),
-        }
-    }
-
-    #[test]
-    fn resp_no_data() {
-        let (s1, _) = UnixDatagram::pair().expect("failed to create socket pair");
-        s1.shutdown(Shutdown::Both).unwrap();
-        match VmResponse::recv(&s1) {
-            Err(e) => {
-                assert_eq!(e, VmControlError::BadSize(0));
-            }
-            _ => panic!("recv wrong response"),
-        }
-    }
-
-    #[test]
-    fn resp_bad_size() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        s2.send_with_fds([12; 7].as_ref(), &[]).unwrap();
-        match VmResponse::recv(&s1) {
-            Err(e) => {
-                assert_eq!(e, VmControlError::BadSize(7));
-            }
-            _ => panic!("recv wrong response"),
-        }
-    }
-
-    #[test]
-    fn resp_invalid_type() {
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        s2.send_with_fds([12; VM_RESPONSE_SIZE].as_ref(), &[])
-            .unwrap();
-        match VmResponse::recv(&s1) {
-            Err(e) => {
-                assert_eq!(e, VmControlError::InvalidType);
-            }
-            _ => panic!("recv wrong response"),
-        }
-    }
-
-    #[test]
-    fn resp_allocate_and_register_gpu_memory() {
-        if !kernel_has_memfd() {
-            return;
-        }
-        let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-        let shm_size: usize = 4096;
-        let mut shm = SharedMemory::new(None).unwrap();
-        shm.set_size(shm_size as u64).unwrap();
-        let memory_pfn = 55;
-        let memory_slot = 66;
-        let memory_planes = [
-            GpuMemoryPlaneDesc {
-                stride: 32,
-                offset: 84,
-            },
-            GpuMemoryPlaneDesc {
-                stride: 48,
-                offset: 96,
-            },
-            GpuMemoryPlaneDesc {
-                stride: 64,
-                offset: 112,
-            },
-        ];
-        let r1 = VmResponse::AllocateAndRegisterGpuMemory {
-            fd: MaybeOwnedFd::Borrowed(shm.as_raw_fd()),
-            pfn: memory_pfn,
-            slot: memory_slot,
-            desc: GpuMemoryDesc {
-                planes: memory_planes,
-            },
-        };
-        r1.send(&s1).unwrap();
-        match VmResponse::recv(&s2).unwrap() {
-            VmResponse::AllocateAndRegisterGpuMemory {
-                fd,
-                pfn,
-                slot,
-                desc,
-            } => {
-                assert!(fd.as_raw_fd() >= 0);
-                assert_eq!(pfn, memory_pfn);
-                assert_eq!(slot, memory_slot);
-                assert_eq!(desc.planes, memory_planes);
-            }
-            _ => panic!("recv wrong response variant"),
-        }
-    }
-}