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>
This commit is contained in:
Jingkui Wang 2018-10-03 13:04:47 -07:00 committed by chrome-bot
parent b23c091c8b
commit e13b180f74
11 changed files with 67 additions and 520 deletions

3
Cargo.lock generated
View file

@ -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",
]

View file

@ -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)
}
}

View file

@ -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" }

View file

@ -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),

View file

@ -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" }

View file

@ -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],
}

View file

@ -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;

View file

@ -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 {

View file

@ -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

View file

@ -10,3 +10,4 @@ kvm = { path = "../kvm" }
libc = "*"
sys_util = { path = "../sys_util" }
resources = { path = "../resources" }
msg_socket = { path = "../msg_socket" }

View file

@ -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"),
}
}
}