From d44320488fd2db344b6b1fd156c22fdf90b82fe2 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Wed, 6 Dec 2017 18:20:09 -0800 Subject: [PATCH] main: Add inflate/deflate interface for balloon Change-Id: I0fc63abbed8db303c7d283ce392fd47777b60d19 Reviewed-on: https://chromium-review.googlesource.com/818207 Commit-Ready: Dylan Reid Tested-by: Dylan Reid Reviewed-by: Zach Reizner --- Cargo.lock | 1 + src/linux.rs | 20 +++++++++++++------- src/main.rs | 33 +++++++++++++++++++++++++++++++++ vm_control/Cargo.toml | 1 + vm_control/src/lib.rs | 29 +++++++++++++++++++++++++++-- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcdcc3da28..8a3ff71173 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,7 @@ dependencies = [ name = "vm_control" version = "0.1.0" dependencies = [ + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "data_model 0.1.0", "kvm 0.1.0", "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/linux.rs b/src/linux.rs index c57f6b3015..c5c7001f0a 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -253,7 +253,7 @@ pub fn run_config(cfg: Config) -> Result<()> { device_manager.register_mmio(rng_box, rng_jail, &mut cmdline) .map_err(Error::RegisterRng)?; - let (_, balloon_device_socket) = UnixDatagram::pair().map_err(Error::Socket)?; + let (balloon_host_socket, balloon_device_socket) = UnixDatagram::pair().map_err(Error::Socket)?; let balloon_box = Box::new(devices::virtio::Balloon::new(balloon_device_socket) .map_err(Error::BalloonDeviceNew)?); let balloon_jail = if cfg.multiprocess { @@ -387,7 +387,8 @@ pub fn run_config(cfg: Config) -> Result<()> { cfg.vcpu_count.unwrap_or(1), guest_mem, &device_manager.bus, - control_sockets) + control_sockets, + balloon_host_socket) } fn run_kvm(requests: Vec, @@ -396,7 +397,8 @@ fn run_kvm(requests: Vec, vcpu_count: u32, guest_mem: GuestMemory, mmio_bus: &devices::Bus, - control_sockets: Vec) + control_sockets: Vec, + balloon_host_socket: UnixDatagram) -> Result<()> { let kvm = Kvm::new().map_err(Error::Kvm)?; let kernel_start_addr = GuestAddress(KERNEL_START_OFFSET); @@ -414,7 +416,8 @@ fn run_kvm(requests: Vec, let mut next_dev_pfn = BASE_DEV_MEMORY_PFN; for request in requests { let mut running = false; - if let VmResponse::Err(e) = request.execute(&mut vm, &mut next_dev_pfn, &mut running) { + if let VmResponse::Err(e) = request.execute(&mut vm, &mut next_dev_pfn, + &mut running, &balloon_host_socket) { return Err(Error::Vm(e)); } if !running { @@ -573,7 +576,8 @@ fn run_kvm(requests: Vec, exit_evt, sigchld_fd, kill_signaled, - vcpu_handles) + vcpu_handles, + balloon_host_socket) } fn run_control(mut vm: Vm, @@ -583,7 +587,8 @@ fn run_control(mut vm: Vm, exit_evt: EventFd, sigchld_fd: SignalFd, kill_signaled: Arc, - vcpu_handles: Vec>) + vcpu_handles: Vec>, + balloon_host_socket: UnixDatagram) -> Result<()> { const MAX_VM_FD_RECV: usize = 1; @@ -665,7 +670,8 @@ fn run_control(mut vm: Vm, Ok(request) => { let mut running = true; let response = - request.execute(&mut vm, &mut next_dev_pfn, &mut running); + request.execute(&mut vm, &mut next_dev_pfn, + &mut running, &balloon_host_socket); if let Err(e) = response.send(&mut scm, socket.as_ref()) { error!("failed to send VmResponse: {:?}", e); } diff --git a/src/main.rs b/src/main.rs index d3c80f7811..68bdb67238 100644 --- a/src/main.rs +++ b/src/main.rs @@ -382,6 +382,36 @@ fn stop_vms(args: std::env::Args) { } } +fn balloon_vms(mut args: std::env::Args) { + let mut scm = Scm::new(1); + if args.len() < 2 { + print_help("crosvm balloon", "PAGE_ADJUST VM_SOCKET...", &[]); + println!("Adjust the ballon size of the crosvm instance by `PAGE_ADJUST` pages, `PAGE_ADJUST` can be negative to shrink the balloon."); + } + let num_pages: i32 = match args.nth(0).unwrap().parse::() { + Ok(n) => n, + Err(_) => { + error!("Failed to parse number of pages"); + return; + }, + }; + + for socket_path in args { + match UnixDatagram::unbound().and_then(|s| { + s.connect(&socket_path)?; + Ok(s) + }) { + Ok(s) => { + if let Err(e) = VmRequest::BalloonAdjust(num_pages).send(&mut scm, &s) { + error!("failed to send balloon request to socket at '{}': {:?}", + socket_path, + e); + } + } + Err(e) => error!("failed to connect to socket at '{}': {}", socket_path, e), + } + } +} fn print_usage() { print_help("crosvm", "[stop|run]", &[]); @@ -410,6 +440,9 @@ fn main() { Some("run") => { run_vm(args); } + Some("balloon") => { + balloon_vms(args); + } Some(c) => { println!("invalid subcommand: {:?}", c); print_usage(); diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml index 06b3695f0d..a0d12f5def 100644 --- a/vm_control/Cargo.toml +++ b/vm_control/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["The Chromium OS Authors"] [dependencies] +byteorder = "*" data_model = { path = "../data_model" } kvm = { path = "../kvm" } libc = "*" diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index cf40569ddc..d486081057 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -10,6 +10,7 @@ //! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor //! if the request type expects one. +extern crate byteorder; extern crate data_model; extern crate kvm; extern crate libc; @@ -22,6 +23,7 @@ use std::result; use libc::{ERANGE, EINVAL}; +use byteorder::{LittleEndian, WriteBytesExt}; use data_model::{DataInit, Le32, Le64, VolatileMemory}; use sys_util::{EventFd, Error as SysError, MmapError, MemoryMapping, Scm, GuestAddress}; use kvm::{IoeventAddress, Vm}; @@ -65,6 +67,8 @@ impl AsRawFd for MaybeOwnedFd { /// /// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success. pub enum VmRequest { + /// Try to grow or shrink the VM's balloon. + BalloonAdjust(i32), /// Break the VM's run loop and exit. Exit, /// Register the given ioevent address along with given datamatch to trigger the `EventFd`. @@ -81,7 +85,8 @@ 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_SIZE: usize = 16; +const VM_REQUEST_TYPE_BALLOON_ADJUST: u32 = 4; +const VM_REQUEST_SIZE: usize = 24; #[repr(C)] #[derive(Clone, Copy, Default)] @@ -89,6 +94,7 @@ struct VmRequestStruct { type_: Le32, slot: Le32, size: Le64, + num_pages: Le32, } // Safe because it only has data and has no implicit padding. @@ -99,6 +105,7 @@ impl VmRequest { /// /// A `VmResponse` should be sent out over the given socket before another request is received. pub fn recv(scm: &mut Scm, s: &UnixDatagram) -> VmControlResult { + assert_eq!(VM_REQUEST_SIZE, std::mem::size_of::()); let mut buf = [0; VM_REQUEST_SIZE]; let mut fds = Vec::new(); let read = scm.recv(s, &mut [&mut buf], &mut fds) @@ -118,6 +125,9 @@ impl VmRequest { 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)) + }, _ => Err(VmControlError::InvalidType), } } @@ -127,6 +137,7 @@ impl VmRequest { /// After this request is a sent, a `VmResponse` should be received before sending another /// request. pub fn send(&self, scm: &mut Scm, s: &UnixDatagram) -> VmControlResult<()> { + assert_eq!(VM_REQUEST_SIZE, std::mem::size_of::()); let mut req = VmRequestStruct::default(); let mut fd_buf = [0; 1]; let mut fd_len = 0; @@ -142,6 +153,10 @@ impl VmRequest { 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); + }, _ => return Err(VmControlError::InvalidType), } let mut buf = [0; VM_REQUEST_SIZE]; @@ -162,7 +177,8 @@ impl VmRequest { /// This does not return a result, instead encapsulating the success or failure in a /// `VmResponse` with the intended purpose of sending the response back over the socket that /// received this `VmRequest`. - pub fn execute(&self, vm: &mut Vm, next_mem_pfn: &mut u64, running: &mut bool) -> VmResponse { + pub fn execute(&self, vm: &mut Vm, next_mem_pfn: &mut u64, running: &mut bool, + balloon_host_socket: &UnixDatagram) -> VmResponse { *running = true; match self { &VmRequest::Exit => { @@ -209,6 +225,15 @@ impl VmRequest { Err(e) => VmResponse::Err(e), } } + &VmRequest::BalloonAdjust(num_pages) => { + let mut buf = [0u8; 4]; + // write_i32 can't fail as the buffer is 4 bytes long. + (&mut buf[0..]).write_i32::(num_pages).unwrap(); + match balloon_host_socket.send(&buf) { + Ok(_) => VmResponse::Ok, + Err(_) => VmResponse::Err(SysError::last()), + } + }, } } }