Слияние кода завершено, страница обновится автоматически
// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate machine_manager;
#[cfg(target_arch = "x86_64")]
#[macro_use]
extern crate vmm_sys_util;
pub mod errors {
error_chain! {
links {
AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind);
IntCtrl(devices::IntCtrlErrs::Error, devices::IntCtrlErrs::ErrorKind) #[cfg(target_arch = "aarch64")];
Legacy(devices::LegacyErrs::Error, devices::LegacyErrs::ErrorKind);
MicroVm(super::micro_vm::errors::Error, super::micro_vm::errors::ErrorKind);
StdVm(super::standard_vm::errors::Error, super::standard_vm::errors::ErrorKind);
Util(util::errors::Error, util::errors::ErrorKind);
Virtio(virtio::errors::Error, virtio::errors::ErrorKind);
MachineManager(machine_manager::config::errors::Error, machine_manager::config::errors::ErrorKind);
Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind);
}
foreign_links {
KvmIoctl(kvm_ioctls::Error);
Io(std::io::Error);
}
errors {
AddDevErr(dev: String) {
display("Failed to add {} device.", dev)
}
LoadKernErr {
display("Failed to load kernel.")
}
CrtMemSpaceErr {
display("Failed to create memory address space")
}
CrtIoSpaceErr {
display("Failed to create I/O address space")
}
RegMemRegionErr(base: u64, size: u64) {
display("Failed to register region in memory space: base={},size={}", base, size)
}
InitEventFdErr(fd: String) {
display("Failed to init eventfd {}.", fd)
}
RlzVirtioMmioErr {
display("Failed to realize virtio mmio.")
}
#[cfg(target_arch = "x86_64")]
CrtIrqchipErr {
display("Failed to create irq chip.")
}
#[cfg(target_arch = "x86_64")]
SetTssErr {
display("Failed to set tss address.")
}
#[cfg(target_arch = "x86_64")]
CrtPitErr {
display("Failed to create PIT.")
}
#[cfg(target_arch = "aarch64")]
GenFdtErr {
display("Failed to generate FDT.")
}
#[cfg(target_arch = "aarch64")]
WrtFdtErr(addr: u64, size: usize) {
display("Failed to write FDT: addr={}, size={}", addr, size)
}
RegNotifierErr {
display("Failed to register event notifier.")
}
StartVcpuErr(id: u8) {
display("Failed to run vcpu{}.", id)
}
PauseVcpuErr(id: u8) {
display("Failed to pause vcpu{}.", id)
}
ResumeVcpuErr(id: u8) {
display("Failed to resume vcpu{}.", id)
}
DestroyVcpuErr(id: u8) {
display("Failed to destroy vcpu{}.", id)
}
}
}
}
mod micro_vm;
mod standard_vm;
pub use micro_vm::LightMachine;
use pci::{PciBus, PciDevOps, PciHost, RootPort};
pub use standard_vm::StdMachine;
use virtio::{
BlockState, RngState, VhostKern, VirtioConsoleState, VirtioDevice, VirtioMmioState,
VirtioNetState,
};
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::sync::{Arc, Barrier, Mutex, Weak};
#[cfg(target_arch = "x86_64")]
use address_space::KvmIoListener;
use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region};
use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU};
use devices::legacy::FwCfgOps;
#[cfg(target_arch = "aarch64")]
use devices::InterruptController;
use hypervisor::kvm::KVM_FDS;
use kvm_ioctls::VcpuFd;
use machine_manager::config::{
get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net,
parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial,
parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig,
FAST_UNPLUG_ON,
};
use machine_manager::event_loop::EventLoop;
use machine_manager::machine::{KvmVmState, MachineInterface};
use migration::MigrationManager;
use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation};
use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter};
use vfio::{VfioDevice, VfioPciDevice};
use virtio::{balloon_allow_list, Balloon, Block, Console, Rng, VirtioMmioDevice, VirtioPciDevice};
use vmm_sys_util::epoll::EventSet;
use vmm_sys_util::eventfd::EventFd;
use errors::{ErrorKind, Result, ResultExt};
use standard_vm::errors::Result as StdResult;
pub trait MachineOps {
/// Calculate the ranges of memory according to architecture.
///
/// # Arguments
///
/// * `mem_size` - memory size of VM.
///
/// # Returns
///
/// A array of ranges, it's element represents (start_addr, size).
/// On x86_64, there is a gap ranged from (4G - 768M) to 4G, which will be skipped.
fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)>;
fn load_boot_source(&self, fwcfg: Option<&Arc<Mutex<dyn FwCfgOps>>>) -> Result<CPUBootConfig>;
/// Init I/O & memory address space and mmap guest memory.
///
/// # Arguments
///
/// * `mem_config` - Memory setting.
/// * `sys_io` - IO address space required for x86_64.
/// * `sys_mem` - Memory address space.
fn init_memory(
&self,
mem_config: &MachineMemConfig,
#[cfg(target_arch = "x86_64")] sys_io: &Arc<AddressSpace>,
sys_mem: &Arc<AddressSpace>,
is_migrate: bool,
nr_cpus: u8,
) -> Result<()> {
// KVM_CREATE_VM system call is invoked when KVM_FDS is used for the first time. The system
// call registers some notifier functions in the KVM, which are frequently triggered when
// doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps
// needs to be invoked first.
let mut mem_mappings = Vec::new();
if !is_migrate {
let ram_ranges = self.arch_ram_ranges(mem_config.mem_size);
mem_mappings = create_host_mmaps(&ram_ranges, &mem_config, nr_cpus)
.chain_err(|| "Failed to mmap guest ram.")?;
}
sys_mem
.register_listener(Arc::new(Mutex::new(KvmMemoryListener::new(
KVM_FDS.load().fd.as_ref().unwrap().get_nr_memslots() as u32,
))))
.chain_err(|| "Failed to register KVM listener for memory space.")?;
#[cfg(target_arch = "x86_64")]
sys_io
.register_listener(Arc::new(Mutex::new(KvmIoListener::default())))
.chain_err(|| "Failed to register KVM listener for I/O address space.")?;
if !is_migrate {
for mmap in mem_mappings.iter() {
let base = mmap.start_address().raw_value();
let size = mmap.size();
sys_mem
.root()
.add_subregion(Region::init_ram_region(mmap.clone()), base)
.chain_err(|| ErrorKind::RegMemRegionErr(base, size))?;
}
}
MigrationManager::register_memory_instance(sys_mem.clone());
Ok(())
}
/// Init vcpu register with boot message.
///
/// # Arguments
///
/// * `vm` - `MachineInterface` to obtain functions cpu can use.
/// * `nr_cpus` - The number of vcpus.
/// * `fds` - File descriptors obtained by creating new Vcpu in KVM.
/// * `boot_cfg` - Boot message generated by reading boot source to guest memory.
fn init_vcpu(
vm: Arc<Mutex<dyn MachineInterface + Send + Sync>>,
nr_cpus: u8,
fds: &[Arc<VcpuFd>],
boot_cfg: &Option<CPUBootConfig>,
) -> Result<Vec<Arc<CPU>>>
where
Self: Sized,
{
let mut cpus = Vec::<Arc<CPU>>::new();
for vcpu_id in 0..nr_cpus {
#[cfg(target_arch = "aarch64")]
let arch_cpu = ArchCPU::new(u32::from(vcpu_id));
#[cfg(target_arch = "x86_64")]
let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(nr_cpus));
let cpu = Arc::new(CPU::new(
fds[vcpu_id as usize].clone(),
vcpu_id,
Arc::new(Mutex::new(arch_cpu)),
vm.clone(),
));
cpus.push(cpu.clone());
MigrationManager::register_device_instance(cpu::ArchCPU::descriptor(), cpu, false);
}
if let Some(boot_config) = boot_cfg {
for cpu_index in 0..nr_cpus as usize {
cpus[cpu_index as usize]
.realize(&boot_config)
.chain_err(|| {
format!(
"Failed to realize arch cpu register for CPU {}/KVM",
cpu_index
)
})?;
}
}
Ok(cpus)
}
/// Add interrupt controller.
///
/// # Arguments
///
/// * `vcpu_count` - The number of vcpu.
fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()>;
/// Add RTC device.
fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] mem_size: u64) -> Result<()>;
/// Add serial device.
///
/// # Arguments
///
/// * `config` - Device configuration.
fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()>;
/// Add block device.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration args.
fn add_virtio_mmio_block(&mut self, _vm_config: &mut VmConfig, _cfg_args: &str) -> Result<()> {
bail!("Virtio mmio devices Not supported!");
}
/// Add virtio mmio vsock device.
///
/// # Arguments
///
/// * `cfg_args` - Device configuration.
fn add_virtio_vsock(&mut self, cfg_args: &str) -> Result<()> {
let device_cfg = parse_vsock(cfg_args)?;
let sys_mem = self.get_sys_mem().clone();
let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem)));
if cfg_args.contains("vhost-vsock-device") {
let device = VirtioMmioDevice::new(&sys_mem, vsock.clone());
MigrationManager::register_device_instance_mutex(
VirtioMmioState::descriptor(),
self.realize_virtio_mmio_device(device)
.chain_err(|| ErrorKind::RlzVirtioMmioErr)?,
);
} else {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let virtio_pci_device = VirtioPciDevice::new(
device_cfg.id,
devfn,
sys_mem,
vsock.clone(),
parent_bus,
multi_func,
);
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci vsock device")?;
}
MigrationManager::register_device_instance_mutex(
VhostKern::VsockState::descriptor(),
vsock,
);
Ok(())
}
fn realize_virtio_mmio_device(
&mut self,
_dev: VirtioMmioDevice,
) -> Result<Arc<Mutex<VirtioMmioDevice>>> {
bail!("Virtio mmio devices not supported");
}
fn get_sys_mem(&mut self) -> &Arc<AddressSpace>;
/// Add net device.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration args.
fn add_virtio_mmio_net(&mut self, _vm_config: &mut VmConfig, _cfg_args: &str) -> Result<()> {
bail!("Virtio mmio device Not supported!");
}
fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let device_cfg = parse_balloon(vm_config, cfg_args)?;
let sys_mem = self.get_sys_mem();
let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone())));
Balloon::object_init(balloon.clone());
if cfg_args.contains("virtio-balloon-device") {
let device = VirtioMmioDevice::new(sys_mem, balloon);
self.realize_virtio_mmio_device(device)?;
} else {
let name = device_cfg.id;
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
let virtio_pci_device =
VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func);
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci balloon device")?;
}
Ok(())
}
/// Add console device.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration args.
fn add_virtio_console(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let device_cfg = parse_virtconsole(vm_config, cfg_args)?;
let sys_mem = self.get_sys_mem();
let console = Arc::new(Mutex::new(Console::new(device_cfg.clone())));
if let Some(serial) = &vm_config.virtio_serial {
if serial.pci_bdf.is_none() {
let device = VirtioMmioDevice::new(sys_mem, console.clone());
MigrationManager::register_device_instance_mutex(
VirtioMmioState::descriptor(),
self.realize_virtio_mmio_device(device)
.chain_err(|| ErrorKind::RlzVirtioMmioErr)?,
);
} else {
let name = device_cfg.id;
let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial {
serial_info
} else {
bail!("No virtio-serial-pci device configured for virtconsole");
};
// Reasonable, because for virtio-serial-pci device, the bdf has been checked.
let bdf = virtio_serial_info.pci_bdf.clone().unwrap();
let multi_func = virtio_serial_info.multifunction;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
let virtio_pci_device = VirtioPciDevice::new(
name,
devfn,
sys_mem,
console.clone(),
parent_bus,
multi_func,
);
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci console device")?;
}
} else {
bail!("No virtio-serial-bus specified");
}
MigrationManager::register_device_instance_mutex(VirtioConsoleState::descriptor(), console);
Ok(())
}
fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
parse_virtio_serial(vm_config, cfg_args)?;
Ok(())
}
/// Add virtio-rng device.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration arguments.
fn add_virtio_rng(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let device_cfg = parse_rng_dev(vm_config, cfg_args)?;
let sys_mem = self.get_sys_mem();
let rng_dev = Arc::new(Mutex::new(Rng::new(device_cfg.clone())));
if cfg_args.contains("virtio-rng-device") {
let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone());
self.realize_virtio_mmio_device(device)
.chain_err(|| "Failed to add virtio mmio rng device")?;
} else {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
let vitio_pci_device = VirtioPciDevice::new(
device_cfg.id,
devfn,
sys_mem,
rng_dev.clone(),
parent_bus,
multi_func,
);
vitio_pci_device
.realize()
.chain_err(|| "Failed to add pci rng device")?;
}
MigrationManager::register_device_instance_mutex(RngState::descriptor(), rng_dev);
Ok(())
}
fn get_pci_host(&mut self) -> StdResult<&Arc<Mutex<PciHost>>> {
bail!("No pci host found");
}
fn check_device_id_existed(&mut self, name: &str) -> Result<()> {
// If there is no pci bus, skip the id check, such as micro vm.
if let Ok(pci_host) = self.get_pci_host() {
// Because device_del needs an id when removing a device, it's necessary to ensure that the id is unique.
if name.is_empty() {
bail!("Device id is empty");
}
if PciBus::find_attached_bus(&pci_host.lock().unwrap().root_bus, name).is_some() {
bail!("Device id {} existed", name);
}
}
Ok(())
}
fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let device_cfg = parse_blk(vm_config, cfg_args)?;
let device = Arc::new(Mutex::new(Block::new(device_cfg.clone())));
self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func)?;
MigrationManager::register_device_instance_mutex(BlockState::descriptor(), device);
self.reset_bus(&device_cfg.id)?;
Ok(())
}
fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let device_cfg = parse_net(vm_config, cfg_args)?;
let device: Arc<Mutex<dyn VirtioDevice>> = if device_cfg.vhost_type.is_some() {
Arc::new(Mutex::new(VhostKern::Net::new(
&device_cfg,
self.get_sys_mem(),
)))
} else {
let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone())));
MigrationManager::register_device_instance_mutex(
VirtioNetState::descriptor(),
device.clone(),
);
device
};
self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func)?;
self.reset_bus(&device_cfg.id)?;
Ok(())
}
fn create_vfio_pci_device(
&mut self,
id: &str,
bdf: &PciBdf,
host: &str,
multifunc: bool,
) -> Result<()> {
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let path = format!("/sys/bus/pci/devices/{}", host);
let device = VfioDevice::new(Path::new(&path), self.get_sys_mem())
.chain_err(|| "Failed to create vfio device.")?;
let vfio_pci = VfioPciDevice::new(
device,
devfn,
id.to_string(),
parent_bus,
multifunc,
self.get_sys_mem().clone(),
);
VfioPciDevice::realize(vfio_pci).chain_err(|| "Failed to realize vfio-pci device.")?;
Ok(())
}
fn add_vfio_device(&mut self, cfg_args: &str) -> Result<()> {
let device_cfg: VfioConfig = parse_vfio(cfg_args)?;
let bdf = get_pci_bdf(cfg_args)?;
let multifunc = get_multi_function(cfg_args)?;
self.create_vfio_pci_device(&device_cfg.id, &bdf, &device_cfg.host, multifunc)?;
self.reset_bus(&device_cfg.id)?;
Ok(())
}
fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> StdResult<(u8, Weak<Mutex<PciBus>>)> {
let pci_host = self.get_pci_host()?;
let bus = pci_host.lock().unwrap().root_bus.clone();
let pci_bus = PciBus::find_bus_by_name(&bus, &bdf.bus);
if pci_bus.is_none() {
bail!("Parent bus :{} not found", &bdf.bus);
}
let parent_bus = Arc::downgrade(&pci_bus.unwrap());
let devfn = (bdf.addr.0 << 3) + bdf.addr.1;
Ok((devfn, parent_bus))
}
fn add_pci_root_port(&mut self, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let device_cfg = parse_root_port(cfg_args)?;
let pci_host = self.get_pci_host()?;
let bus = pci_host.lock().unwrap().root_bus.clone();
if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() {
bail!("ID {} already exists.");
}
let rootport = RootPort::new(
device_cfg.id,
devfn,
device_cfg.port,
parent_bus,
device_cfg.multifunction,
);
rootport
.realize()
.chain_err(|| "Failed to add pci root port")?;
Ok(())
}
fn add_virtio_pci_device(
&mut self,
id: &str,
bdf: &PciBdf,
device: Arc<Mutex<dyn VirtioDevice>>,
multi_func: bool,
) -> Result<()> {
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem();
let pcidev = VirtioPciDevice::new(
id.to_string(),
devfn,
sys_mem.clone(),
device,
parent_bus,
multi_func,
);
pcidev
.realize()
.chain_err(|| "Failed to add virtio pci device")?;
Ok(())
}
/// Set the parent bus slot on when device attached
fn reset_bus(&mut self, dev_id: &str) -> Result<()> {
let pci_host = self.get_pci_host()?;
let locked_pci_host = pci_host.lock().unwrap();
let bus =
if let Some((bus, _)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &dev_id) {
bus
} else {
bail!("Bus not found, dev id {}", dev_id);
};
let locked_bus = bus.lock().unwrap();
if locked_bus.name == "pcie.0" {
// No need to reset root bus
return Ok(());
}
let parent_bridge = if let Some(bridge) = locked_bus.parent_bridge.as_ref() {
bridge
} else {
bail!("Parent bridge does not exist, dev id {}", dev_id);
};
let dev = parent_bridge.upgrade().unwrap();
let locked_dev = dev.lock().unwrap();
let name = locked_dev.name();
drop(locked_dev);
let mut devfn = None;
let locked_bus = locked_pci_host.root_bus.lock().unwrap();
for (id, dev) in &locked_bus.devices {
if dev.lock().unwrap().name() == name {
devfn = Some(*id);
break;
}
}
drop(locked_bus);
// It's safe to call devfn.unwrap(), because the bus exists.
match locked_pci_host.find_device(0, devfn.unwrap()) {
Some(dev) => dev
.lock()
.unwrap()
.reset(false)
.chain_err(|| "Failed to reset bus"),
None => bail!("Failed to found device"),
}
}
/// Init vm global config.
///
/// # Arguments
///
/// * `vm_config` - VM Configuration.
fn init_global_config(&mut self, vm_config: &mut VmConfig) -> Result<()> {
let fast_unplug = vm_config
.global_config
.get("pcie-root-port.fast-unplug")
.map_or(false, |val| val == FAST_UNPLUG_ON);
RootPort::set_fast_unplug_feature(fast_unplug);
Ok(())
}
/// Add peripheral devices.
///
/// # Arguments
///
/// * `vm_config` - VM Configuration.
fn add_devices(&mut self, vm_config: &mut VmConfig) -> Result<()> {
self.add_rtc_device(
#[cfg(target_arch = "x86_64")]
vm_config.machine_config.mem_config.mem_size,
)
.chain_err(|| ErrorKind::AddDevErr("RTC".to_string()))?;
let cloned_vm_config = vm_config.clone();
if let Some(serial) = cloned_vm_config.serial.as_ref() {
self.add_serial_device(serial)
.chain_err(|| ErrorKind::AddDevErr("serial".to_string()))?;
}
if let Some(pflashs) = cloned_vm_config.pflashs.as_ref() {
self.add_pflash_device(pflashs)
.chain_err(|| ErrorKind::AddDevErr("pflash".to_string()))?;
}
for dev in &cloned_vm_config.devices {
let cfg_args = dev.1.as_str();
// Check whether the device id exists to ensure device uniqueness.
let id = parse_device_id(cfg_args)?;
self.check_device_id_existed(&id)
.chain_err(|| format!("Failed to check device id: config {}", cfg_args))?;
match dev.0.as_str() {
"virtio-blk-device" => {
self.add_virtio_mmio_block(vm_config, cfg_args)?;
}
"virtio-blk-pci" => {
self.add_virtio_pci_blk(vm_config, cfg_args)?;
}
"virtio-net-device" => {
self.add_virtio_mmio_net(vm_config, cfg_args)?;
}
"virtio-net-pci" => {
self.add_virtio_pci_net(vm_config, cfg_args)?;
}
"pcie-root-port" => {
self.add_pci_root_port(cfg_args)?;
}
"vhost-vsock-pci" | "vhost-vsock-device" => {
self.add_virtio_vsock(cfg_args)?;
}
"virtio-balloon-device" | "virtio-balloon-pci" => {
self.add_virtio_balloon(vm_config, cfg_args)?;
}
"virtio-serial-device" | "virtio-serial-pci" => {
self.add_virtio_serial(vm_config, cfg_args)?;
}
"virtconsole" => {
self.add_virtio_console(vm_config, cfg_args)?;
}
"virtio-rng-device" | "virtio-rng-pci" => {
self.add_virtio_rng(vm_config, cfg_args)?;
}
"vfio-pci" => {
self.add_vfio_device(cfg_args)?;
}
_ => {
bail!("Unsupported device: {:?}", dev.0.as_str());
}
}
}
Ok(())
}
fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> {
bail!("Pflash device is not supported!");
}
/// Return the syscall whitelist for seccomp.
fn syscall_whitelist(&self) -> Vec<BpfRule>;
/// Register seccomp rules in syscall whitelist to seccomp.
fn register_seccomp(&self, balloon_enable: bool) -> Result<()> {
let mut seccomp_filter = SyscallFilter::new(SeccompOpt::Trap);
let mut bpf_rules = self.syscall_whitelist();
if balloon_enable {
balloon_allow_list(&mut bpf_rules);
}
for bpf_rule in &mut bpf_rules {
seccomp_filter.push(bpf_rule);
}
seccomp_filter
.realize()
.chain_err(|| "Failed to init seccomp filter.")?;
Ok(())
}
/// Register event notifier for power button of mainboard.
///
/// # Arguments
///
/// * `power_button` - Eventfd of the power button.
fn register_power_event(&self, power_button: &EventFd) -> Result<()> {
let power_button = power_button.try_clone().unwrap();
let button_fd = power_button.as_raw_fd();
let power_button_handler: Arc<Mutex<Box<NotifierCallback>>> =
Arc::new(Mutex::new(Box::new(move |_, _| {
let _ret = power_button.read().unwrap();
None
})));
let notifier = EventNotifier::new(
NotifierOperation::AddShared,
button_fd,
None,
EventSet::IN,
vec![power_button_handler],
);
EventLoop::update_event(vec![notifier], None).chain_err(|| ErrorKind::RegNotifierErr)?;
Ok(())
}
/// Realize the machine.
///
/// # Arguments
///
/// * `vm` - The machine structure.
/// * `vm_config` - VM configuration.
fn realize(vm: &Arc<Mutex<Self>>, vm_config: &mut VmConfig, is_migrate: bool) -> Result<()>
where
Self: Sized;
/// Run `LightMachine` with `paused` flag.
///
/// # Arguments
///
/// * `paused` - Flag for `paused` when `LightMachine` starts to run.
fn run(&self, paused: bool) -> Result<()>;
/// Start machine as `Running` or `Paused` state.
///
/// # Arguments
///
/// * `paused` - After started, paused all vcpu or not.
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm kvm vm state.
fn vm_start(paused: bool, cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()>
where
Self: Sized,
{
let nr_vcpus = cpus.len();
let cpus_thread_barrier = Arc::new(Barrier::new((nr_vcpus + 1) as usize));
for cpu_index in 0..nr_vcpus {
let cpu_thread_barrier = cpus_thread_barrier.clone();
let cpu = cpus[cpu_index as usize].clone();
CPU::start(cpu, cpu_thread_barrier, paused)
.chain_err(|| format!("Failed to run vcpu{}", cpu_index))?;
}
if paused {
*vm_state = KvmVmState::Paused;
} else {
*vm_state = KvmVmState::Running;
}
cpus_thread_barrier.wait();
Ok(())
}
/// Pause VM as `Paused` state, sleepy all vcpu thread.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm kvm vm state.
fn vm_pause(
cpus: &[Arc<CPU>],
#[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>,
vm_state: &mut KvmVmState,
) -> Result<()>
where
Self: Sized,
{
for (cpu_index, cpu) in cpus.iter().enumerate() {
cpu.pause()
.chain_err(|| format!("Failed to pause vcpu{}", cpu_index))?;
}
#[cfg(target_arch = "aarch64")]
irq_chip.as_ref().unwrap().stop();
*vm_state = KvmVmState::Paused;
Ok(())
}
/// Resume VM as `Running` state, awaken all vcpu thread.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm kvm vm state.
fn vm_resume(cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()>
where
Self: Sized,
{
for (cpu_index, cpu) in cpus.iter().enumerate() {
cpu.resume()
.chain_err(|| format!("Failed to resume vcpu{}", cpu_index))?;
}
*vm_state = KvmVmState::Running;
Ok(())
}
/// Destroy VM as `Shutdown` state, destroy vcpu thread.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm kvm vm state.
fn vm_destroy(cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()>
where
Self: Sized,
{
for (cpu_index, cpu) in cpus.iter().enumerate() {
cpu.destroy()
.chain_err(|| format!("Failed to destroy vcpu{}", cpu_index))?;
}
*vm_state = KvmVmState::Shutdown;
Ok(())
}
/// Transfer VM state from `old` to `new`.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm kvm vm state.
/// * `old_state` - Old vm state want to leave.
/// * `new_state` - New vm state want to transfer to.
fn vm_state_transfer(
cpus: &[Arc<CPU>],
#[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>,
vm_state: &mut KvmVmState,
old_state: KvmVmState,
new_state: KvmVmState,
) -> Result<()>
where
Self: Sized,
{
use KvmVmState::*;
if *vm_state != old_state {
bail!("Vm lifecycle error: state check failed.");
}
match (old_state, new_state) {
(Created, Running) => <Self as MachineOps>::vm_start(false, cpus, vm_state)
.chain_err(|| "Failed to start vm.")?,
(Running, Paused) => <Self as MachineOps>::vm_pause(
cpus,
#[cfg(target_arch = "aarch64")]
irq_chip,
vm_state,
)
.chain_err(|| "Failed to pause vm.")?,
(Paused, Running) => <Self as MachineOps>::vm_resume(cpus, vm_state)
.chain_err(|| "Failed to resume vm.")?,
(_, Shutdown) => {
<Self as MachineOps>::vm_destroy(cpus, vm_state)
.chain_err(|| "Failed to destroy vm.")?;
}
(_, _) => {
bail!("Vm lifecycle error: this transform is illegal.");
}
}
if *vm_state != new_state {
bail!(
"Vm lifecycle error: state '{:?} -> {:?}' transform failed.",
old_state,
new_state
);
}
Ok(())
}
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )