From 20d3c6a32f244ad3f7c140687902643a02e6c017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Wed, 26 Nov 2025 13:23:35 +0100 Subject: [PATCH] Add dynamic CID allocation for Linux VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each Linux VM now gets a unique CID (Context ID) for vsock communication, preventing address collisions when running multiple VMs concurrently. Follows the existing IpGuard RAII pattern with CidGuard for automatic cleanup. macOS VMs are unaffected as the Virtualization framework handles CID allocation automatically. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- runner/src/resource_manager.rs | 80 ++++++++++++++++++++++++++++++++++ runner/src/vm_impl/linux.rs | 19 ++++++-- runner/src/vsock.rs | 15 ------- 3 files changed, 96 insertions(+), 18 deletions(-) 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;