From 25c8d5db5de0b878ab7902e352e41a495fc6d042 Mon Sep 17 00:00:00 2001 From: Pavel Mores Date: Mon, 18 Sep 2023 10:36:15 +0200 Subject: [PATCH] runtime-rs: use qemu cmdline generation framework to launch VM Deploy the framework added by the previous commit to generate qemu command line and launch the VM. We now properly store the child process object which allows us to implement remaining Hypervisor functions necessary for a simple but successful VM lifecycle, get_vmm_master_tid() and stop_vm(). Fixes #8184 Signed-off-by: Pavel Mores --- .../crates/hypervisor/src/qemu/inner.rs | 113 +++++++++++++----- .../crates/hypervisor/src/qemu/mod.rs | 1 + 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs index da775ecc72..32ec6bc0ca 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs @@ -3,19 +3,21 @@ // SPDX-License-Identifier: Apache-2.0 // -use anyhow::{anyhow, Context, Result}; - +use super::cmdline_generator::QemuCmdLine; use crate::{ - hypervisor_persist::HypervisorState, HypervisorConfig, MemoryConfig, - VcpuThreadIds, VsockDevice, HYPERVISOR_QEMU, + hypervisor_persist::HypervisorState, HypervisorConfig, MemoryConfig, VcpuThreadIds, + VsockDevice, HYPERVISOR_QEMU, }; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; use kata_types::{ capabilities::{Capabilities, CapabilityBits}, config::KATA_PATH, }; -use async_trait::async_trait; -use std::collections::HashMap; use persist::sandbox_persist::Persist; +use std::collections::HashMap; +use std::os::unix::io::AsRawFd; +use std::process::Child; const VSOCK_SCHEME: &str = "vsock"; @@ -24,6 +26,8 @@ pub struct QemuInner { /// sandbox id id: String, + qemu_process: Option, + config: HypervisorConfig, devices: Vec, } @@ -32,6 +36,7 @@ impl QemuInner { pub fn new() -> QemuInner { QemuInner { id: "".to_string(), + qemu_process: None, config: Default::default(), devices: Vec::new(), } @@ -39,15 +44,8 @@ impl QemuInner { pub(crate) async fn prepare_vm(&mut self, id: &str, _netns: Option) -> Result<()> { info!(sl!(), "Preparing QEMU VM"); - self.id = id.to_string(); - self.devices.push(DeviceType::Vsock( - VsockDevice::new(self.id.clone()) - .await - .context("qemu: create agent vsock")?, - )); - Ok(()) } @@ -57,28 +55,72 @@ impl QemuInner { let vm_path = [KATA_PATH, self.id.as_str()].join("/"); std::fs::create_dir_all(vm_path)?; + let mut cmdline = QemuCmdLine::new(&self.id, &self.config)?; + + // If there's a Vsock Device in `self.devices` the vhost-vsock file + // descriptor needs to stay open until the qemu process launches. + // This is why we need to store it in a variable at this scope. + let mut _vhost_fd = None; + + for device in &mut self.devices { + match device { + DeviceType::ShareFs(share_fs_dev) => { + if share_fs_dev.config.fs_type == "virtio-fs" { + cmdline.add_virtiofs_share( + &share_fs_dev.config.sock_path, + &share_fs_dev.config.mount_tag, + share_fs_dev.config.queue_size, + ); + } + } + DeviceType::Vsock(vsock_dev) => { + let fd = vsock_dev.init_config().await?; + cmdline.add_vsock(fd.as_raw_fd(), vsock_dev.config.guest_cid)?; + _vhost_fd = Some(fd); + } + DeviceType::Block(block_dev) => { + if block_dev.config.path_on_host == self.config.boot_info.initrd { + // If this block device represents initrd we ignore it here, it + // will be handled elsewhere by adding `-initrd` to the qemu + // command line. + continue; + } + match block_dev.config.driver_option.as_str() { + "nvdimm" => cmdline.add_nvdimm( + &block_dev.config.path_on_host, + block_dev.config.is_readonly, + )?, + unsupported => { + info!(sl!(), "unsupported block device driver: {}", unsupported) + } + } + } + _ => info!(sl!(), "qemu cmdline: unsupported device: {:?}", device), + } + } + // To get access to the VM console for debugging, enable the following + // line and replace its argument appropriately (open a terminal, run + // `tty` in it to get its device file path and use it as the argument). + //cmdline.add_serial_console("/dev/pts/23"); + + info!(sl!(), "qemu args: {}", cmdline.build().await?.join(" ")); let mut command = std::process::Command::new(&self.config.path); + command.args(cmdline.build().await?); - command - .arg("-kernel") - .arg(&self.config.boot_info.kernel) - .arg("-m") - .arg(format!("{}M", &self.config.memory_info.default_memory)) - .arg("-initrd") - .arg(&self.config.boot_info.initrd) - .arg("-vga") - .arg("none") - .arg("-nodefaults") - .arg("-nographic"); - - command.spawn()?; + info!(sl!(), "qemu cmd: {:?}", command); + self.qemu_process = Some(command.spawn()?); Ok(()) } pub(crate) fn stop_vm(&mut self) -> Result<()> { info!(sl!(), "Stopping QEMU VM"); - todo!() + if let Some(ref mut qemu_process) = &mut self.qemu_process { + info!(sl!(), "QemuInner::stop_vm(): kill()'ing qemu"); + qemu_process.kill().map_err(anyhow::Error::from) + } else { + Err(anyhow!("qemu process not running")) + } } pub(crate) fn pause_vm(&self) -> Result<()> { @@ -121,7 +163,16 @@ impl QemuInner { pub(crate) async fn get_vmm_master_tid(&self) -> Result { info!(sl!(), "QemuInner::get_vmm_master_tid()"); - todo!() + if let Some(qemu_process) = &self.qemu_process { + info!( + sl!(), + "QemuInner::get_vmm_master_tid(): returning {}", + qemu_process.id() + ); + Ok(qemu_process.id()) + } else { + Err(anyhow!("qemu process not running")) + } } pub(crate) async fn get_ns_path(&self) -> Result { @@ -237,11 +288,7 @@ impl QemuInner { fn get_agent_vsock_dev(&self) -> Option<&VsockDevice> { self.devices.iter().find_map(|dev| { if let DeviceType::Vsock(vsock_dev) = dev { - if vsock_dev.id == self.id { - Some(vsock_dev) - } else { - None - } + Some(vsock_dev) } else { None } diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs b/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs index 7b712f140c..16921fcee1 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // +mod cmdline_generator; mod inner; use crate::device::DeviceType;