Skip to content

Commit 521a2ba

Browse files
committed
feat(cli): add --gpu all to select every present GPU
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent 2de916e commit 521a2ba

1 file changed

Lines changed: 39 additions & 5 deletions

File tree

crates/sandlock-cli/src/main.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,34 @@ enum Command {
4040
/// Sandbox-level flags come from `SandboxBuilder` via `#[clap(flatten)]`.
4141
/// CLI-only flags (profile, timeout, image, etc.) and non-clap-friendly
4242
/// sandbox fields (max_memory, fs_mount, env, gpu, cpu-cores) remain here.
43+
/// GPU visibility for `--gpu`: `all` for every present GPU, or explicit
44+
/// indices like `0,2`. Absence of the flag means no GPU access.
45+
#[derive(Clone, Debug)]
46+
enum GpuSelection {
47+
All,
48+
Devices(Vec<u32>),
49+
}
50+
51+
impl std::str::FromStr for GpuSelection {
52+
type Err = String;
53+
54+
fn from_str(s: &str) -> Result<Self, Self::Err> {
55+
if s.eq_ignore_ascii_case("all") {
56+
return Ok(GpuSelection::All);
57+
}
58+
let devices = s
59+
.split(',')
60+
.map(|p| {
61+
let p = p.trim();
62+
p.parse::<u32>().map_err(|_| {
63+
format!("invalid GPU index '{p}'; expected 'all' or indices like 0,2")
64+
})
65+
})
66+
.collect::<Result<Vec<_>, _>>()?;
67+
Ok(GpuSelection::Devices(devices))
68+
}
69+
}
70+
4371
#[derive(clap::Args)]
4472
struct RunArgs {
4573
// ── Sandbox flags (flattened from SandboxBuilder) ───────────────────────
@@ -78,9 +106,9 @@ struct RunArgs {
78106
#[arg(long = "cpu-cores", value_delimiter = ',')]
79107
cpu_cores: Vec<u32>,
80108

81-
/// GPU device indices visible to the sandbox (e.g. --gpu 0,2)
82-
#[arg(long = "gpu", value_delimiter = ',')]
83-
gpu_devices: Vec<u32>,
109+
/// GPUs visible to the sandbox: `all` for every GPU, or indices (e.g. --gpu 0,2)
110+
#[arg(long = "gpu", value_name = "all|N,N,...")]
111+
gpu: Option<GpuSelection>,
84112

85113
// ── CLI-only flags ───────────────────────────────────────────────────────
86114
#[arg(short = 't', long)]
@@ -466,7 +494,13 @@ async fn run_command(args: RunArgs) -> Result<i32> {
466494
};
467495
}
468496
if !args.cpu_cores.is_empty() { builder = builder.cpu_cores(args.cpu_cores.clone()); }
469-
if !args.gpu_devices.is_empty() { builder = builder.gpu_devices(args.gpu_devices.clone()); }
497+
match &args.gpu {
498+
// `all` maps to an empty device list, which the core expands to every
499+
// present GPU; explicit indices pass through unchanged.
500+
Some(GpuSelection::All) => builder = builder.gpu_devices(Vec::new()),
501+
Some(GpuSelection::Devices(devs)) => builder = builder.gpu_devices(devs.clone()),
502+
None => {}
503+
}
470504
for spec in &args.env_vars {
471505
if let Some((k, v)) = spec.split_once('=') {
472506
builder = builder.env_var(k, v);
@@ -694,7 +728,7 @@ fn validate_no_supervisor(args: &RunArgs) -> Result<()> {
694728
if args.max_disk.is_some() { bad.push("--max-disk"); }
695729
if pb.port_remap { bad.push("--port-remap"); }
696730
if !args.cpu_cores.is_empty() { bad.push("--cpu-cores"); }
697-
if !args.gpu_devices.is_empty() { bad.push("--gpu"); }
731+
if args.gpu.is_some() { bad.push("--gpu"); }
698732
if args.dry_run { bad.push("--dry-run"); }
699733
if args.status_fd.is_some() { bad.push("--status-fd"); }
700734
if !pb.fs_denied.is_empty() { bad.push("--fs-deny"); }

0 commit comments

Comments
 (0)