Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/cli/src/commands/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ pub struct PoolStartArgs {
#[arg(long)]
pub deferred: bool,

/// Mark pooled VM memory KSM-mergeable so the host dedups identical pages
/// across same-image VMs (Linux 6.4+; needs /sys/kernel/mm/ksm/run=1).
#[arg(long)]
pub ksm: bool,

/// Output as JSON
#[arg(long)]
pub json: bool,
Expand Down Expand Up @@ -265,6 +270,8 @@ struct PoolRegistry {
/// When true, pooled VMs boot IDLE and `pool run` spawns the command as the
/// box's real MAIN (full box semantics), instead of exec-into-keepalive.
deferred: bool,
/// Mark pooled VM memory KSM-mergeable (host page dedup across same-image VMs).
ksm: bool,
}

impl PoolRegistry {
Expand Down Expand Up @@ -292,6 +299,7 @@ impl PoolRegistry {
cmd: keepalive_cmd(),
pool: pool_config.clone(),
deferred_main: self.deferred,
ksm: self.ksm,
..Default::default()
};
let pool = std::sync::Arc::new(
Expand Down Expand Up @@ -360,6 +368,7 @@ async fn execute_start(args: PoolStartArgs) -> Result<(), Box<dyn std::error::Er
max: args.max,
ttl: args.ttl,
deferred: args.deferred,
ksm: args.ksm,
});

// Pre-warm the default image, if one was given.
Expand Down Expand Up @@ -892,6 +901,7 @@ mod tests {
socket: DEFAULT_SOCKET.to_string(),
warm: vec![],
deferred: false,
ksm: false,
json: false,
};
let result = execute_start(args).await;
Expand All @@ -909,6 +919,7 @@ mod tests {
socket: DEFAULT_SOCKET.to_string(),
warm: vec![],
deferred: false,
ksm: false,
json: false,
};
let result = execute_start(args).await;
Expand Down
7 changes: 7 additions & 0 deletions src/core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ pub struct BoxConfig {
#[serde(default)]
pub deferred_main: bool,

/// Mark guest memory KSM-mergeable so the host kernel dedups identical pages
/// across same-image VMs (Linux 6.4+; needs /sys/kernel/mm/ksm/run=1 on the
/// host). Most valuable for pools of same-image sandboxes.
#[serde(default)]
pub ksm: bool,

/// Port mappings: "host_port:guest_port" (e.g., "8080:80")
/// Maps host ports to guest ports via TSI (Transparent Socket Impersonation).
#[serde(default)]
Expand Down Expand Up @@ -415,6 +421,7 @@ impl Default for BoxConfig {
cache: CacheConfig::default(),
pool: PoolConfig::default(),
deferred_main: false,
ksm: false,
port_map: vec![],
dns: vec![],
add_hosts: vec![],
Expand Down
7 changes: 7 additions & 0 deletions src/core/src/vmm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ pub struct InstanceSpec {
/// Guest agent entrypoint
pub entrypoint: Entrypoint,

/// Mark guest memory KSM-mergeable (host page dedup across same-image VMs;
/// Linux 6.4+, requires /sys/kernel/mm/ksm/run=1 on the host).
#[serde(default)]
pub ksm: bool,

/// Optional console output file path
pub console_output: Option<PathBuf>,

Expand Down Expand Up @@ -175,6 +180,7 @@ impl Default for InstanceSpec {
args: Vec::new(),
env: Vec::new(),
},
ksm: false,
console_output: None,
workdir: "/".to_string(),
tee_config: None,
Expand Down Expand Up @@ -395,6 +401,7 @@ mod tests {
fn test_instance_spec_serde_roundtrip() {
let spec = InstanceSpec {
box_id: "test-box-123".to_string(),
ksm: false,
vcpus: 4,
memory_mib: 2048,
rootfs_path: PathBuf::from("/tmp/rootfs"),
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/src/vm/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ impl VmManager {
network: None, // Network config is set by CLI when --network is specified
resource_limits: self.config.resource_limits.clone(),
log_config: self.log_config.clone(),
// KSM page-merging: config field, or the A3S_BOX_KSM env override.
ksm: self.config.ksm
|| std::env::var("A3S_BOX_KSM")
.map(|v| matches!(v.as_str(), "1" | "true" | "yes" | "on"))
.unwrap_or(false),
})
}

Expand Down
6 changes: 6 additions & 0 deletions src/runtime/src/vmm/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ impl VmmProvider for VmController {
cmd.arg("--config").arg(&config_json).stdin(Stdio::null());
self.configure_shim_stdio(&mut cmd, spec);

// KSM page-merging: the shim opts its (guest) memory in via prctl when this
// env is set; driven by InstanceSpec.ksm (BoxConfig.ksm or A3S_BOX_KSM).
if spec.ksm {
cmd.env("A3S_BOX_KSM", "1");
}

// On macOS, set DYLD_LIBRARY_PATH to help find libkrunfw
#[cfg(target_os = "macos")]
{
Expand Down
Loading