mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-12-25 04:14:06 +00:00
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:
parent
b23c091c8b
commit
e13b180f74
11 changed files with 67 additions and 520 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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],
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
17
src/linux.rs
17
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,3 +10,4 @@ kvm = { path = "../kvm" }
|
|||
libc = "*"
|
||||
sys_util = { path = "../sys_util" }
|
||||
resources = { path = "../resources" }
|
||||
msg_socket = { path = "../msg_socket" }
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue