diff --git a/runner/src/resource_manager.rs b/runner/src/resource_manager.rs index 3e50358..9470310 100644 --- a/runner/src/resource_manager.rs +++ b/runner/src/resource_manager.rs @@ -175,6 +175,8 @@ pub struct ResourceManager { allocation: Arc>, // IP pool - queue of available IPs free_ips: Arc>>, + // CID pool - queue of available Context IDs for vsock (Linux VMs only) + free_cids: Arc>>, } impl ResourceManager { @@ -187,10 +189,28 @@ impl ResourceManager { free_ips.push_back(Ipv4Addr::new(10, 0, 0, i)); } + // Initialize CID pool with a range of CIDs (3 - 1024) + // + // The Context Identifier (CID) is a 32-bit address for vsock communication. + // The combination of a CID and a port number uniquely identifies a vsock connection. + // + // There are several special addresses: + // -1U32: VMADDR_CID_ANY is used to indicate any CID. + // 0: VMADDR_CID_HYPERVISOR is reserved for the hypervisor. + // 1: VMADDR_CID_LOCAL is the well-known address for local communication (loopback). + // 2: VMADDR_CID_HOST is the well-known address of the host. + // + // Guest CIDs range from 3 to max(2^32 - 1). + let mut free_cids = VecDeque::new(); + for cid in 3..=1024 { + free_cids.push_back(cid); + } + Self { limits, allocation: Arc::new(RwLock::new(ResourceState::new())), free_ips: Arc::new(RwLock::new(free_ips)), + free_cids: Arc::new(RwLock::new(free_cids)), } } @@ -356,6 +376,31 @@ impl ResourceManager { free_ips.len() ); } + + /// Allocate a CID (Context ID) from the pool for vsock communication + pub async fn allocate_cid(&self) -> Option { + let mut free_cids = self.free_cids.write().await; + let cid = free_cids.pop_front(); + + if let Some(cid) = cid { + tracing::debug!( + "Allocated CID: {} ({} CIDs remaining)", + cid, + free_cids.len() + ); + } else { + tracing::warn!("No available CIDs in pool"); + } + + cid + } + + /// Release a CID back to the pool + pub async fn release_cid(&self, cid: u32) { + let mut free_cids = self.free_cids.write().await; + free_cids.push_back(cid); + tracing::debug!("Released CID: {} ({} CIDs available)", cid, free_cids.len()); + } } /// RAII guard for IP address allocation @@ -398,6 +443,41 @@ impl Drop for IpGuard { } } +/// RAII guard for CID allocation (Linux VMs only) +/// Automatically releases the CID when dropped +pub struct CidGuard { + cid: Option, + resource_manager: Arc, +} + +impl CidGuard { + /// Create a new CID guard by allocating a CID from the resource manager + pub async fn new(resource_manager: Arc) -> Option { + let cid = resource_manager.allocate_cid().await?; + Some(Self { + cid: Some(cid), + resource_manager, + }) + } + + /// Get the allocated CID + pub fn cid(&self) -> Option { + self.cid + } +} + +impl Drop for CidGuard { + fn drop(&mut self) { + if let Some(cid) = self.cid { + // We need to spawn a task to release the CID since drop is not async + let resource_manager = self.resource_manager.clone(); + tokio::spawn(async move { + resource_manager.release_cid(cid).await; + }); + } + } +} + /// RAII guard for allocated resources /// Automatically releases resources when dropped pub struct ResourceGuard { diff --git a/runner/src/vm_impl/linux.rs b/runner/src/vm_impl/linux.rs index f9f6374..551b58b 100644 --- a/runner/src/vm_impl/linux.rs +++ b/runner/src/vm_impl/linux.rs @@ -1,7 +1,7 @@ use crate::protocol::{JobConfig, VM}; -use crate::resource_manager::{IpGuard, ResourceGuard, ResourceManager}; +use crate::resource_manager::{CidGuard, IpGuard, ResourceGuard, ResourceManager}; use crate::vm::Vm; -use crate::vsock::{self, GUEST_CID}; +use crate::vsock; use cloud_hypervisor_client::apis::client::APIClient; use cloud_hypervisor_client::apis::configuration::Configuration; use cloud_hypervisor_client::models::console_config::Mode; @@ -34,6 +34,8 @@ pub struct LinuxVm { _resource_guard: Option, /// IP guard for automatic IP release (kept alive for RAII cleanup) _ip_guard: Option, + /// CID guard for automatic CID release (kept alive for RAII cleanup) + _cid_guard: Option, /// The VM identifier id: String, /// The runtime directory for VM-related files (as TempDir for auto-cleanup) @@ -244,8 +246,18 @@ impl Vm for LinuxVm { tracing::info!("Allocated IP {} for VM {}", guest_ip, id); + // Allocate CID (Context ID) for vsock communication (required for Linux) + let cid_guard = CidGuard::new(resource_manager.clone()) + .await + .ok_or_else(|| eyre::eyre!("Failed to allocate CID for VM {}", id))?; + let cid = cid_guard + .cid() + .ok_or_else(|| eyre::eyre!("CID guard has no CID"))?; + + tracing::debug!("Allocated CID {} for VM {}", cid, id); + let vsock_config = VsockConfig { - cid: GUEST_CID as i64, + cid: cid as i64, socket: vsock_socket_path.to_str().unwrap().to_string(), iommu: Some(false), ..Default::default() @@ -316,6 +328,7 @@ impl Vm for LinuxVm { config: vm_config, _resource_guard: Some(resource_guard), _ip_guard: Some(ip_guard), + _cid_guard: Some(cid_guard), id, _run_dir_temp: run_dir_temp, run_dir, diff --git a/runner/src/vsock.rs b/runner/src/vsock.rs index 6b4f7fe..0402e44 100644 --- a/runner/src/vsock.rs +++ b/runner/src/vsock.rs @@ -14,21 +14,6 @@ use tokio::sync::{Mutex, Notify, mpsc}; use tokio_vsock::{VsockAddr, VsockStream}; use tracing::{error, info}; -// The Context Identifier (CID) is a 32-bit address for vsock communication. -// The combination of a CID and a port number uniquely identifies a vsock connection. -// -// There are several special addresses: -// -// -1U32: VMADDR_CID_ANY is used to indicate any CID. -// 0: VMADDR_CID_HYPERVISOR is reserved for the hypervisor. -// 1: VMADDR_CID_LOCAL is the well-known address for local communication (loopback). -// 2: VMADDR_CID_HOST is the well-known address of the host. -// -// Guest CIDs range from 3 to max(2^32 - 1). -// -// TODO: if running multiple VMs, this value will be different for each VM. -pub const GUEST_CID: u32 = 3; - /// The CID for the host. pub const HOST_CID: u32 = tokio_vsock::VMADDR_CID_HOST;