2024-01-30 17:42:55 -08:00
|
|
|
pub mod error;
|
2024-03-10 00:36:23 -08:00
|
|
|
|
2024-01-30 17:42:55 -08:00
|
|
|
use crate::error::{Error, Result};
|
2024-06-21 10:38:19 -07:00
|
|
|
use log::{debug, trace};
|
|
|
|
use pci::PciBdf;
|
2024-03-30 03:49:13 +00:00
|
|
|
use tokio::time::timeout;
|
2024-06-21 10:38:19 -07:00
|
|
|
use tx::ClientTransaction;
|
|
|
|
use xenplatform::boot::BootSetupPlatform;
|
|
|
|
use xenplatform::domain::{BaseDomainConfig, BaseDomainManager, CreatedDomain};
|
2024-01-30 17:42:55 -08:00
|
|
|
|
2024-01-21 01:58:07 -08:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::str::FromStr;
|
2024-06-20 19:42:45 -07:00
|
|
|
use std::sync::Arc;
|
2024-01-21 01:58:07 -08:00
|
|
|
use std::time::Duration;
|
2024-01-30 17:42:55 -08:00
|
|
|
use xencall::XenCall;
|
2024-06-21 10:38:19 -07:00
|
|
|
use xenstore::{XsdClient, XsdInterface};
|
2024-01-09 15:40:17 -08:00
|
|
|
|
2024-04-29 10:02:20 -07:00
|
|
|
pub mod pci;
|
2024-06-21 10:38:19 -07:00
|
|
|
pub mod tx;
|
2024-04-29 10:02:20 -07:00
|
|
|
|
2024-04-02 00:56:18 +00:00
|
|
|
#[derive(Clone)]
|
2024-06-20 19:42:45 -07:00
|
|
|
pub struct XenClient<P: BootSetupPlatform> {
|
2024-01-21 01:58:07 -08:00
|
|
|
pub store: XsdClient,
|
2024-06-29 15:43:08 -07:00
|
|
|
pub call: XenCall,
|
2024-06-21 10:38:19 -07:00
|
|
|
domain_manager: Arc<BaseDomainManager<P>>,
|
2024-01-09 15:40:17 -08:00
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2024-01-21 00:13:05 -08:00
|
|
|
pub struct BlockDeviceRef {
|
|
|
|
pub path: String,
|
|
|
|
pub major: u32,
|
|
|
|
pub minor: u32,
|
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainDisk {
|
|
|
|
pub vdev: String,
|
|
|
|
pub block: BlockDeviceRef,
|
2024-01-18 06:15:42 -08:00
|
|
|
pub writable: bool,
|
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainFilesystem {
|
|
|
|
pub path: String,
|
|
|
|
pub tag: String,
|
2024-01-22 02:15:53 -08:00
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainNetworkInterface {
|
|
|
|
pub mac: String,
|
2024-02-01 13:56:03 +00:00
|
|
|
pub mtu: u32,
|
2024-04-22 12:48:45 -07:00
|
|
|
pub bridge: Option<String>,
|
|
|
|
pub script: Option<String>,
|
2024-02-01 13:56:03 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2024-03-27 06:28:47 +00:00
|
|
|
pub struct DomainChannel {
|
|
|
|
pub typ: String,
|
|
|
|
pub initialized: bool,
|
|
|
|
}
|
2024-02-06 14:35:55 +00:00
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainEventChannel {
|
|
|
|
pub name: String,
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|
|
|
|
|
2024-04-29 10:02:20 -07:00
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
|
|
pub enum DomainPciRdmReservePolicy {
|
|
|
|
Invalid,
|
|
|
|
#[default]
|
|
|
|
Strict,
|
|
|
|
Relaxed,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DomainPciRdmReservePolicy {
|
|
|
|
pub fn to_option_str(&self) -> &str {
|
|
|
|
match self {
|
|
|
|
DomainPciRdmReservePolicy::Invalid => "-1",
|
|
|
|
DomainPciRdmReservePolicy::Strict => "0",
|
|
|
|
DomainPciRdmReservePolicy::Relaxed => "1",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainPciDevice {
|
|
|
|
pub bdf: PciBdf,
|
|
|
|
pub permissive: bool,
|
|
|
|
pub msi_translate: bool,
|
|
|
|
pub power_management: bool,
|
|
|
|
pub rdm_reserve_policy: DomainPciRdmReservePolicy,
|
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct DomainConfig {
|
2024-06-21 10:38:19 -07:00
|
|
|
pub base: BaseDomainConfig,
|
2024-01-18 06:15:42 -08:00
|
|
|
pub backend_domid: u32,
|
2024-04-22 12:48:45 -07:00
|
|
|
pub name: String,
|
|
|
|
pub disks: Vec<DomainDisk>,
|
2024-06-20 19:42:45 -07:00
|
|
|
pub swap_console_backend: Option<String>,
|
2024-03-27 06:28:47 +00:00
|
|
|
pub channels: Vec<DomainChannel>,
|
2024-04-22 12:48:45 -07:00
|
|
|
pub vifs: Vec<DomainNetworkInterface>,
|
|
|
|
pub filesystems: Vec<DomainFilesystem>,
|
2024-04-29 10:02:20 -07:00
|
|
|
pub pcis: Vec<DomainPciDevice>,
|
2024-01-21 01:58:07 -08:00
|
|
|
pub extra_keys: Vec<(String, String)>,
|
2024-02-23 04:48:44 +00:00
|
|
|
pub extra_rw_paths: Vec<String>,
|
2024-01-17 12:36:13 -08:00
|
|
|
}
|
|
|
|
|
2024-03-27 06:28:47 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct CreatedChannel {
|
|
|
|
pub ring_ref: u64,
|
|
|
|
pub evtchn: u32,
|
|
|
|
}
|
|
|
|
|
2024-05-05 13:39:53 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2024-06-20 19:42:45 -07:00
|
|
|
impl<P: BootSetupPlatform> XenClient<P> {
|
|
|
|
pub async fn new(current_domid: u32, platform: P) -> Result<XenClient<P>> {
|
2024-02-23 04:37:53 +00:00
|
|
|
let store = XsdClient::open().await?;
|
2024-06-21 10:38:19 -07:00
|
|
|
let call: XenCall = XenCall::open(current_domid)?;
|
|
|
|
let domain_manager = BaseDomainManager::new(call.clone(), platform).await?;
|
2024-06-20 19:42:45 -07:00
|
|
|
Ok(XenClient {
|
|
|
|
store,
|
|
|
|
call,
|
2024-06-21 10:38:19 -07:00
|
|
|
domain_manager: Arc::new(domain_manager),
|
2024-06-20 19:42:45 -07:00
|
|
|
})
|
2024-01-09 15:40:17 -08:00
|
|
|
}
|
|
|
|
|
2024-04-22 12:48:45 -07:00
|
|
|
pub async fn create(&self, config: &DomainConfig) -> Result<CreatedDomain> {
|
2024-06-21 10:38:19 -07:00
|
|
|
let created = self.domain_manager.create(config.base.clone()).await?;
|
|
|
|
match self.init(created.domid, config, &created).await {
|
|
|
|
Ok(_) => Ok(created),
|
2024-01-20 20:29:36 -08:00
|
|
|
Err(err) => {
|
|
|
|
// ignore since destroying a domain is best
|
|
|
|
// effort when an error occurs
|
2024-06-21 10:38:19 -07:00
|
|
|
let _ = self.domain_manager.destroy(created.domid).await;
|
2024-01-20 20:29:36 -08:00
|
|
|
Err(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-21 10:38:19 -07:00
|
|
|
pub async fn transaction(&self, domid: u32, backend_domid: u32) -> Result<ClientTransaction> {
|
|
|
|
ClientTransaction::new(&self.store, domid, backend_domid).await
|
|
|
|
}
|
2024-01-17 05:22:47 -08:00
|
|
|
|
2024-06-21 10:38:19 -07:00
|
|
|
async fn init(&self, domid: u32, config: &DomainConfig, created: &CreatedDomain) -> Result<()> {
|
|
|
|
trace!("xenclient init domid={} domain={:?}", domid, created);
|
|
|
|
let transaction = self.transaction(domid, config.backend_domid).await?;
|
|
|
|
transaction
|
|
|
|
.add_domain_declaration(&config.name, &config.base, created)
|
2024-02-23 04:37:53 +00:00
|
|
|
.await?;
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction.commit().await?;
|
2024-01-18 06:15:42 -08:00
|
|
|
if !self
|
|
|
|
.store
|
2024-06-21 10:38:19 -07:00
|
|
|
.introduce_domain(domid, created.store_mfn, created.store_evtchn)
|
2024-02-23 04:37:53 +00:00
|
|
|
.await?
|
2024-01-18 06:15:42 -08:00
|
|
|
{
|
2024-01-30 18:02:32 -08:00
|
|
|
return Err(Error::IntroduceDomainFailed);
|
2024-01-18 06:15:42 -08:00
|
|
|
}
|
2024-06-21 10:38:19 -07:00
|
|
|
let transaction = self.transaction(domid, config.backend_domid).await?;
|
|
|
|
transaction
|
|
|
|
.add_channel_device(
|
|
|
|
created,
|
|
|
|
0,
|
|
|
|
&DomainChannel {
|
|
|
|
typ: config
|
|
|
|
.swap_console_backend
|
|
|
|
.clone()
|
|
|
|
.unwrap_or("xenconsoled".to_string())
|
|
|
|
.to_string(),
|
|
|
|
initialized: true,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await?;
|
2024-05-05 13:39:53 -07:00
|
|
|
|
2024-03-27 06:28:47 +00:00
|
|
|
for (index, channel) in config.channels.iter().enumerate() {
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction
|
|
|
|
.add_channel_device(created, index + 1, channel)
|
|
|
|
.await?;
|
2024-02-06 14:35:55 +00:00
|
|
|
}
|
|
|
|
|
2024-01-18 06:15:42 -08:00
|
|
|
for (index, disk) in config.disks.iter().enumerate() {
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction.add_vbd_device(index, disk).await?;
|
2024-01-18 06:15:42 -08:00
|
|
|
}
|
2024-02-01 13:56:03 +00:00
|
|
|
|
2024-01-22 02:15:53 -08:00
|
|
|
for (index, filesystem) in config.filesystems.iter().enumerate() {
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction.add_9pfs_device(index, filesystem).await?;
|
2024-01-22 02:15:53 -08:00
|
|
|
}
|
2024-02-01 13:56:03 +00:00
|
|
|
|
|
|
|
for (index, vif) in config.vifs.iter().enumerate() {
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction.add_vif_device(index, vif).await?;
|
2024-02-01 13:56:03 +00:00
|
|
|
}
|
2024-02-23 03:25:06 +00:00
|
|
|
|
2024-04-29 10:02:20 -07:00
|
|
|
for (index, pci) in config.pcis.iter().enumerate() {
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction
|
|
|
|
.add_pci_device(&self.call, index, config.pcis.len(), pci)
|
2024-04-02 00:56:18 +00:00
|
|
|
.await?;
|
2024-04-29 10:02:20 -07:00
|
|
|
}
|
|
|
|
|
2024-06-21 10:38:19 -07:00
|
|
|
for (key, value) in &config.extra_keys {
|
|
|
|
transaction.write_key(key, value).await?;
|
2024-05-05 13:39:53 -07:00
|
|
|
}
|
|
|
|
|
2024-06-21 10:38:19 -07:00
|
|
|
for key in &config.extra_rw_paths {
|
|
|
|
transaction.add_rw_path(key).await?;
|
2024-04-29 10:02:20 -07:00
|
|
|
}
|
|
|
|
|
2024-06-21 10:38:19 -07:00
|
|
|
transaction.commit().await?;
|
|
|
|
self.call.unpause_domain(domid).await?;
|
2024-01-09 15:40:17 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-01-21 04:49:31 -08:00
|
|
|
|
2024-04-02 00:56:18 +00:00
|
|
|
pub async fn destroy(&self, domid: u32) -> Result<()> {
|
2024-06-21 10:38:19 -07:00
|
|
|
let _ = self.destroy_store(domid).await;
|
|
|
|
self.domain_manager.destroy(domid).await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-04-02 00:56:18 +00:00
|
|
|
async fn destroy_store(&self, domid: u32) -> Result<()> {
|
2024-02-23 04:37:53 +00:00
|
|
|
let dom_path = self.store.get_domain_path(domid).await?;
|
|
|
|
let vm_path = self.store.read_string(&format!("{}/vm", dom_path)).await?;
|
|
|
|
if vm_path.is_none() {
|
2024-02-23 03:25:06 +00:00
|
|
|
return Err(Error::DomainNonExistent);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut backend_paths: Vec<String> = Vec::new();
|
|
|
|
let console_frontend_path = format!("{}/console", dom_path);
|
|
|
|
let console_backend_path = self
|
|
|
|
.store
|
2024-02-23 04:37:53 +00:00
|
|
|
.read_string(format!("{}/backend", console_frontend_path).as_str())
|
|
|
|
.await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
|
|
|
|
for device_category in self
|
|
|
|
.store
|
2024-02-23 04:37:53 +00:00
|
|
|
.list(format!("{}/device", dom_path).as_str())
|
|
|
|
.await?
|
2024-02-23 03:25:06 +00:00
|
|
|
{
|
|
|
|
for device_id in self
|
|
|
|
.store
|
2024-02-23 04:37:53 +00:00
|
|
|
.list(format!("{}/device/{}", dom_path, device_category).as_str())
|
|
|
|
.await?
|
2024-02-23 03:25:06 +00:00
|
|
|
{
|
|
|
|
let device_path = format!("{}/device/{}/{}", dom_path, device_category, device_id);
|
2024-02-23 04:37:53 +00:00
|
|
|
let Some(backend_path) = self
|
2024-02-23 03:25:06 +00:00
|
|
|
.store
|
2024-02-23 04:37:53 +00:00
|
|
|
.read_string(format!("{}/backend", device_path).as_str())
|
|
|
|
.await?
|
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
};
|
2024-02-23 03:25:06 +00:00
|
|
|
backend_paths.push(backend_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for backend in &backend_paths {
|
|
|
|
let state_path = format!("{}/state", backend);
|
2024-03-30 03:49:13 +00:00
|
|
|
let mut watch = self.store.create_watch(&state_path).await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
let online_path = format!("{}/online", backend);
|
2024-03-07 04:14:25 -08:00
|
|
|
let tx = self.store.transaction().await?;
|
2024-02-23 04:37:53 +00:00
|
|
|
let state = tx.read_string(&state_path).await?.unwrap_or(String::new());
|
2024-02-23 03:25:06 +00:00
|
|
|
if state.is_empty() {
|
|
|
|
break;
|
|
|
|
}
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.write_string(&online_path, "0").await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.write_string(&state_path, "5").await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|
2024-03-30 03:49:13 +00:00
|
|
|
self.store.bind_watch(&watch).await?;
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.commit().await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
|
|
|
|
let mut count: u32 = 0;
|
|
|
|
loop {
|
2024-03-30 03:49:13 +00:00
|
|
|
if count >= 3 {
|
|
|
|
debug!("unable to safely destroy backend: {}", backend);
|
2024-02-23 03:25:06 +00:00
|
|
|
break;
|
|
|
|
}
|
2024-03-30 03:49:13 +00:00
|
|
|
let _ = timeout(Duration::from_secs(1), watch.receiver.recv()).await;
|
|
|
|
let state = self
|
|
|
|
.store
|
|
|
|
.read_string(&state_path)
|
|
|
|
.await?
|
|
|
|
.unwrap_or_else(|| "6".to_string());
|
2024-02-23 03:25:06 +00:00
|
|
|
let state = i64::from_str(&state).unwrap_or(-1);
|
|
|
|
if state == 6 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-07 04:14:25 -08:00
|
|
|
let tx = self.store.transaction().await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
let mut backend_removals: Vec<String> = Vec::new();
|
|
|
|
backend_removals.extend_from_slice(backend_paths.as_slice());
|
|
|
|
if let Some(backend) = console_backend_path {
|
|
|
|
backend_removals.push(backend);
|
|
|
|
}
|
|
|
|
for path in &backend_removals {
|
|
|
|
let path = PathBuf::from(path);
|
|
|
|
let parent = path.parent().ok_or(Error::PathParentNotFound)?;
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.rm(parent.to_str().ok_or(Error::PathStringConversion)?)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
if let Some(vm_path) = vm_path {
|
|
|
|
tx.rm(&vm_path).await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.rm(&dom_path).await?;
|
|
|
|
tx.commit().await?;
|
2024-02-23 03:25:06 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-01-09 15:40:17 -08:00
|
|
|
}
|