mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-27 15:57:09 +00:00
dragonball: add device manager.
Device manager to manage IO devices for a virtual machine. And added DeviceManagerTx to provide operation transaction for device management, added DeviceManagerContext to operation context for device management. Fixes: #4257 Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> Signed-off-by: wllenyj <wllenyj@linux.alibaba.com> Signed-off-by: Chao Wu <chaowu@linux.alibaba.com>
This commit is contained in:
parent
c1c1e5152a
commit
52d42af636
@ -16,8 +16,10 @@ dbs-address-space = "0.1.0"
|
||||
dbs-allocator = "0.1.0"
|
||||
dbs-boot = "0.2.0"
|
||||
dbs-device = "0.1.0"
|
||||
dbs-interrupt = { version = "0.1.0", features = ["kvm-irq"] }
|
||||
dbs-legacy-devices = "0.1.0"
|
||||
dbs-utils = "0.1.0"
|
||||
dbs-virtio-devices = { version = "0.1.0", optional = true, features = ["virtio-mmio"] }
|
||||
kvm-bindings = "0.5.0"
|
||||
kvm-ioctls = "0.11.0"
|
||||
libc = "0.2.39"
|
||||
@ -31,6 +33,7 @@ slog = "2.5.2"
|
||||
slog-scope = "4.4.0"
|
||||
thiserror = "1"
|
||||
vmm-sys-util = "0.9.0"
|
||||
virtio-queue = { version = "0.1.0", optional = true }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap"] }
|
||||
|
||||
[dev-dependencies]
|
||||
@ -42,3 +45,4 @@ atomic-guest-memory = []
|
||||
|
||||
[patch.'crates-io']
|
||||
dbs-legacy-devices = { git = "https://github.com/openanolis/dragonball-sandbox.git", rev = "8e1181eca897b5c5e8e6ac45e3a5c995461865be" }
|
||||
dbs-virtio-devices = { git = "https://github.com/openanolis/dragonball-sandbox.git", rev = "8e1181eca897b5c5e8e6ac45e3a5c995461865be" }
|
||||
|
@ -3,6 +3,24 @@
|
||||
|
||||
//! Device manager to manage IO devices for a virtual machine.
|
||||
|
||||
use std::io;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use dbs_address_space::AddressSpace;
|
||||
use dbs_device::device_manager::{Error as IoManagerError, IoManager, IoManagerContext};
|
||||
use dbs_device::resources::Resource;
|
||||
use dbs_device::DeviceIo;
|
||||
use dbs_interrupt::KvmIrqManager;
|
||||
use dbs_legacy_devices::ConsoleHandler;
|
||||
use dbs_utils::epoll_manager::EpollManager;
|
||||
use kvm_ioctls::VmFd;
|
||||
|
||||
use crate::address_space_manager::GuestAddressSpaceImpl;
|
||||
use crate::error::StartMicrovmError;
|
||||
use crate::resource_manager::ResourceManager;
|
||||
use crate::vm::KernelConfigInfo;
|
||||
|
||||
/// Virtual machine console device manager.
|
||||
pub mod console_manager;
|
||||
/// Console Manager for virtual machines console device.
|
||||
@ -14,10 +32,396 @@ pub use self::legacy::{Error as LegacyDeviceError, LegacyDeviceManager};
|
||||
/// Errors related to device manager operations.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeviceMgrError {
|
||||
/// Invalid operation.
|
||||
#[error("invalid device manager operation")]
|
||||
InvalidOperation,
|
||||
/// Failed to get device resource.
|
||||
#[error("failed to get device assigned resources")]
|
||||
GetDeviceResource,
|
||||
/// Appending to kernel command line failed.
|
||||
#[error("failed to add kernel command line parameter for device: {0}")]
|
||||
Cmdline(#[source] linux_loader::cmdline::Error),
|
||||
/// Failed to manage console devices.
|
||||
#[error(transparent)]
|
||||
ConsoleManager(console_manager::ConsoleManagerError),
|
||||
/// Failed to create the device.
|
||||
#[error("failed to create virtual device: {0}")]
|
||||
CreateDevice(#[source] io::Error),
|
||||
/// Failed to perform an operation on the bus.
|
||||
#[error(transparent)]
|
||||
IoManager(IoManagerError),
|
||||
/// Failure from legacy device manager.
|
||||
#[error(transparent)]
|
||||
LegacyManager(legacy::Error),
|
||||
}
|
||||
|
||||
/// Specialized version of `std::result::Result` for device manager operations.
|
||||
pub type Result<T> = ::std::result::Result<T, DeviceMgrError>;
|
||||
|
||||
/// Type of the dragonball virtio mmio devices.
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
pub type DbsMmioV2Device =
|
||||
MmioV2Device<GuestAddressSpaceImpl, virtio_queue::QueueState, vm_memory::GuestRegionMmap>;
|
||||
|
||||
/// Struct to support transactional operations for device management.
|
||||
pub struct DeviceManagerTx {
|
||||
io_manager: IoManager,
|
||||
_io_lock: Arc<Mutex<()>>,
|
||||
_guard: MutexGuard<'static, ()>,
|
||||
}
|
||||
|
||||
impl DeviceManagerTx {
|
||||
fn new(mgr_ctx: &DeviceManagerContext) -> Self {
|
||||
// Do not expect poisoned lock.
|
||||
let guard = mgr_ctx.io_lock.lock().unwrap();
|
||||
|
||||
// It's really a heavy burden to carry on a lifetime parameter for MutexGuard.
|
||||
// So we play a tricky here that we hold a reference to the Arc<Mutex<()>> and transmute
|
||||
// the MutexGuard<'a, ()> to MutexGuard<'static, ()>.
|
||||
// It's safe because we hold a reference to the Mutex lock.
|
||||
let guard =
|
||||
unsafe { std::mem::transmute::<MutexGuard<'_, ()>, MutexGuard<'static, ()>>(guard) };
|
||||
|
||||
DeviceManagerTx {
|
||||
io_manager: mgr_ctx.io_manager.load().as_ref().clone(),
|
||||
_io_lock: mgr_ctx.io_lock.clone(),
|
||||
_guard: guard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operation context for device management.
|
||||
#[derive(Clone)]
|
||||
pub struct DeviceManagerContext {
|
||||
io_manager: Arc<ArcSwap<IoManager>>,
|
||||
io_lock: Arc<Mutex<()>>,
|
||||
}
|
||||
|
||||
impl DeviceManagerContext {
|
||||
/// Create a DeviceManagerContext object.
|
||||
pub fn new(io_manager: Arc<ArcSwap<IoManager>>, io_lock: Arc<Mutex<()>>) -> Self {
|
||||
DeviceManagerContext {
|
||||
io_manager,
|
||||
io_lock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoManagerContext for DeviceManagerContext {
|
||||
type Context = DeviceManagerTx;
|
||||
|
||||
fn begin_tx(&self) -> Self::Context {
|
||||
DeviceManagerTx::new(self)
|
||||
}
|
||||
|
||||
fn commit_tx(&self, context: Self::Context) {
|
||||
self.io_manager.store(Arc::new(context.io_manager));
|
||||
}
|
||||
|
||||
fn cancel_tx(&self, context: Self::Context) {
|
||||
drop(context);
|
||||
}
|
||||
|
||||
fn register_device_io(
|
||||
&self,
|
||||
ctx: &mut Self::Context,
|
||||
device: Arc<dyn DeviceIo>,
|
||||
resources: &[Resource],
|
||||
) -> std::result::Result<(), dbs_device::device_manager::Error> {
|
||||
ctx.io_manager.register_device_io(device, resources)
|
||||
}
|
||||
|
||||
fn unregister_device_io(
|
||||
&self,
|
||||
ctx: &mut Self::Context,
|
||||
resources: &[Resource],
|
||||
) -> std::result::Result<(), dbs_device::device_manager::Error> {
|
||||
ctx.io_manager.unregister_device_io(resources)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for device addition/removal operations.
|
||||
pub struct DeviceOpContext {
|
||||
epoll_mgr: Option<EpollManager>,
|
||||
io_context: DeviceManagerContext,
|
||||
irq_manager: Arc<KvmIrqManager>,
|
||||
res_manager: Arc<ResourceManager>,
|
||||
vm_fd: Arc<VmFd>,
|
||||
vm_as: Option<GuestAddressSpaceImpl>,
|
||||
address_space: Option<AddressSpace>,
|
||||
logger: slog::Logger,
|
||||
is_hotplug: bool,
|
||||
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
virtio_devices: Vec<Arc<DbsMmioV2Device>>,
|
||||
}
|
||||
|
||||
impl DeviceOpContext {
|
||||
pub(crate) fn new(
|
||||
epoll_mgr: Option<EpollManager>,
|
||||
device_mgr: &DeviceManager,
|
||||
vm_as: Option<GuestAddressSpaceImpl>,
|
||||
address_space: Option<AddressSpace>,
|
||||
is_hotplug: bool,
|
||||
) -> Self {
|
||||
let irq_manager = device_mgr.irq_manager.clone();
|
||||
let res_manager = device_mgr.res_manager.clone();
|
||||
|
||||
let vm_fd = device_mgr.vm_fd.clone();
|
||||
let io_context = DeviceManagerContext {
|
||||
io_manager: device_mgr.io_manager.clone(),
|
||||
io_lock: device_mgr.io_lock.clone(),
|
||||
};
|
||||
let logger = device_mgr.logger.new(slog::o!());
|
||||
|
||||
DeviceOpContext {
|
||||
epoll_mgr,
|
||||
io_context,
|
||||
irq_manager,
|
||||
res_manager,
|
||||
vm_fd,
|
||||
vm_as,
|
||||
address_space,
|
||||
logger,
|
||||
is_hotplug,
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
virtio_devices: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_vm_as(&self) -> Result<GuestAddressSpaceImpl> {
|
||||
match self.vm_as.as_ref() {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(DeviceMgrError::InvalidOperation),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn logger(&self) -> &slog::Logger {
|
||||
&self.logger
|
||||
}
|
||||
|
||||
fn generate_kernel_boot_args(&mut self, kernel_config: &mut KernelConfigInfo) -> Result<()> {
|
||||
if !self.is_hotplug {
|
||||
return Err(DeviceMgrError::InvalidOperation);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
let cmdline = kernel_config.kernel_cmdline_mut();
|
||||
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
for device in self.virtio_devices.iter() {
|
||||
let (mmio_base, mmio_size, irq) = DeviceManager::get_virtio_device_info(device)?;
|
||||
|
||||
// as per doc, [virtio_mmio.]device=<size>@<baseaddr>:<irq> needs to be appended
|
||||
// to kernel commandline for virtio mmio devices to get recognized
|
||||
// the size parameter has to be transformed to KiB, so dividing hexadecimal value in
|
||||
// bytes to 1024; further, the '{}' formatting rust construct will automatically
|
||||
// transform it to decimal
|
||||
cmdline
|
||||
.insert(
|
||||
"virtio_mmio.device",
|
||||
&format!("{}K@0x{:08x}:{}", mmio_size / 1024, mmio_base, irq),
|
||||
)
|
||||
.map_err(DeviceMgrError::Cmdline)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Device manager for virtual machines, which manages all device for a virtual machine.
|
||||
pub struct DeviceManager {
|
||||
io_manager: Arc<ArcSwap<IoManager>>,
|
||||
io_lock: Arc<Mutex<()>>,
|
||||
irq_manager: Arc<KvmIrqManager>,
|
||||
res_manager: Arc<ResourceManager>,
|
||||
vm_fd: Arc<VmFd>,
|
||||
pub(crate) logger: slog::Logger,
|
||||
|
||||
pub(crate) con_manager: ConsoleManager,
|
||||
pub(crate) legacy_manager: Option<LegacyDeviceManager>,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
/// Create a new device manager instance.
|
||||
pub fn new(
|
||||
vm_fd: Arc<VmFd>,
|
||||
res_manager: Arc<ResourceManager>,
|
||||
epoll_manager: EpollManager,
|
||||
logger: &slog::Logger,
|
||||
) -> Self {
|
||||
DeviceManager {
|
||||
io_manager: Arc::new(ArcSwap::new(Arc::new(IoManager::new()))),
|
||||
io_lock: Arc::new(Mutex::new(())),
|
||||
irq_manager: Arc::new(KvmIrqManager::new(vm_fd.clone())),
|
||||
res_manager,
|
||||
vm_fd,
|
||||
logger: logger.new(slog::o!()),
|
||||
con_manager: ConsoleManager::new(epoll_manager, logger),
|
||||
legacy_manager: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the underline interrupt manager for the device manager.
|
||||
pub fn create_interrupt_manager(&mut self) -> Result<()> {
|
||||
self.irq_manager
|
||||
.initialize()
|
||||
.map_err(DeviceMgrError::CreateDevice)
|
||||
}
|
||||
|
||||
/// Get the underlying logger.
|
||||
pub fn logger(&self) -> &slog::Logger {
|
||||
&self.logger
|
||||
}
|
||||
|
||||
/// Create legacy devices associted virtual machine
|
||||
pub fn create_legacy_devices(
|
||||
&mut self,
|
||||
ctx: &mut DeviceOpContext,
|
||||
) -> std::result::Result<(), StartMicrovmError> {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
let mut tx = ctx.io_context.begin_tx();
|
||||
let legacy_manager =
|
||||
LegacyDeviceManager::create_manager(&mut tx.io_manager, Some(self.vm_fd.clone()));
|
||||
|
||||
match legacy_manager {
|
||||
Ok(v) => {
|
||||
self.legacy_manager = Some(v);
|
||||
ctx.io_context.commit_tx(tx);
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.io_context.cancel_tx(tx);
|
||||
return Err(StartMicrovmError::LegacyDevice(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Init legacy devices with logger stream in associted virtual machine
|
||||
pub fn init_legacy_devices(
|
||||
&mut self,
|
||||
dmesg_fifo: Option<Box<dyn io::Write + Send>>,
|
||||
com1_sock_path: Option<String>,
|
||||
_ctx: &mut DeviceOpContext,
|
||||
) -> std::result::Result<(), StartMicrovmError> {
|
||||
// Connect serial ports to the console and dmesg_fifo.
|
||||
self.set_guest_kernel_log_stream(dmesg_fifo)
|
||||
.map_err(|_| StartMicrovmError::EventFd)?;
|
||||
|
||||
slog::info!(self.logger, "init console path: {:?}", com1_sock_path);
|
||||
if let Some(path) = com1_sock_path {
|
||||
if let Some(legacy_manager) = self.legacy_manager.as_ref() {
|
||||
let com1 = legacy_manager.get_com1_serial();
|
||||
self.con_manager
|
||||
.create_socket_console(com1, path)
|
||||
.map_err(StartMicrovmError::DeviceManager)?;
|
||||
}
|
||||
} else if let Some(legacy_manager) = self.legacy_manager.as_ref() {
|
||||
let com1 = legacy_manager.get_com1_serial();
|
||||
self.con_manager
|
||||
.create_stdio_console(com1)
|
||||
.map_err(StartMicrovmError::DeviceManager)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the stream for guest kernel log.
|
||||
///
|
||||
/// Note: com2 is used for guest kernel logging.
|
||||
/// TODO: check whether it works with aarch64.
|
||||
pub fn set_guest_kernel_log_stream(
|
||||
&self,
|
||||
stream: Option<Box<dyn io::Write + Send>>,
|
||||
) -> std::result::Result<(), io::Error> {
|
||||
if let Some(legacy) = self.legacy_manager.as_ref() {
|
||||
legacy
|
||||
.get_com2_serial()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_output_stream(stream);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Restore legacy devices
|
||||
pub fn restore_legacy_devices(
|
||||
&mut self,
|
||||
dmesg_fifo: Option<Box<dyn io::Write + Send>>,
|
||||
com1_sock_path: Option<String>,
|
||||
) -> std::result::Result<(), StartMicrovmError> {
|
||||
self.set_guest_kernel_log_stream(dmesg_fifo)
|
||||
.map_err(|_| StartMicrovmError::EventFd)?;
|
||||
slog::info!(self.logger, "restore console path: {:?}", com1_sock_path);
|
||||
// TODO: restore console
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the console into canonical mode.
|
||||
pub fn reset_console(&self) -> Result<()> {
|
||||
self.con_manager.reset_console()
|
||||
}
|
||||
|
||||
/// Create all registered devices when booting the associated virtual machine.
|
||||
pub fn create_devices(
|
||||
&mut self,
|
||||
vm_as: GuestAddressSpaceImpl,
|
||||
epoll_mgr: EpollManager,
|
||||
kernel_config: &mut KernelConfigInfo,
|
||||
com1_sock_path: Option<String>,
|
||||
dmesg_fifo: Option<Box<dyn io::Write + Send>>,
|
||||
address_space: Option<&AddressSpace>,
|
||||
) -> std::result::Result<(), StartMicrovmError> {
|
||||
let mut ctx = DeviceOpContext::new(
|
||||
Some(epoll_mgr),
|
||||
self,
|
||||
Some(vm_as),
|
||||
address_space.cloned(),
|
||||
false,
|
||||
);
|
||||
|
||||
self.create_legacy_devices(&mut ctx)?;
|
||||
self.init_legacy_devices(dmesg_fifo, com1_sock_path, &mut ctx)?;
|
||||
|
||||
ctx.generate_kernel_boot_args(kernel_config)
|
||||
.map_err(StartMicrovmError::DeviceManager)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Get the underlying eventfd for vm exit notification.
|
||||
pub fn get_reset_eventfd(&self) -> Result<vmm_sys_util::eventfd::EventFd> {
|
||||
if let Some(legacy) = self.legacy_manager.as_ref() {
|
||||
legacy
|
||||
.get_reset_eventfd()
|
||||
.map_err(DeviceMgrError::LegacyManager)
|
||||
} else {
|
||||
Err(DeviceMgrError::LegacyManager(legacy::Error::EventFd(
|
||||
io::Error::from_raw_os_error(libc::ENOENT),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbs-virtio-devices")]
|
||||
impl DeviceManager {
|
||||
fn get_virtio_device_info(device: &Arc<DbsMmioV2Device>) -> Result<(u64, u64, u32)> {
|
||||
let resources = device.get_assigned_resources();
|
||||
let irq = resources
|
||||
.get_legacy_irq()
|
||||
.ok_or(DeviceMgrError::GetDeviceResource)?;
|
||||
let mmio_address_range = device.get_trapped_io_resources().get_mmio_address_ranges();
|
||||
|
||||
// Assume the first MMIO region is virtio configuration region.
|
||||
// Virtio-fs needs to pay attention to this assumption.
|
||||
if let Some(range) = mmio_address_range.into_iter().next() {
|
||||
Ok((range.0, range.1, irq))
|
||||
} else {
|
||||
Err(DeviceMgrError::GetDeviceResource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/dragonball/src/error.rs
Normal file
26
src/dragonball/src/error.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file
|
||||
|
||||
//! Error codes for the virtual machine monitor subsystem.
|
||||
|
||||
/// Errors associated with starting the instance.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum StartMicrovmError {
|
||||
/// The device manager was not configured.
|
||||
#[error("the device manager failed to manage devices: {0}")]
|
||||
DeviceManager(#[source] crate::device_manager::DeviceMgrError),
|
||||
|
||||
/// Cannot add devices to the Legacy I/O Bus.
|
||||
#[error("failure in managing legacy device: {0}")]
|
||||
LegacyDevice(#[source] crate::device_manager::LegacyDeviceError),
|
||||
|
||||
/// Cannot read from an Event file descriptor.
|
||||
#[error("failure while reading from EventFd file descriptor")]
|
||||
EventFd,
|
||||
}
|
@ -12,6 +12,8 @@ pub mod address_space_manager;
|
||||
pub mod config_manager;
|
||||
/// Device manager for virtual machines.
|
||||
pub mod device_manager;
|
||||
/// Errors related to Virtual machine manager.
|
||||
pub mod error;
|
||||
/// Resource manager for virtual machines.
|
||||
pub mod resource_manager;
|
||||
/// Virtual machine manager for virtual machines.
|
||||
|
Loading…
Reference in New Issue
Block a user