-
Notifications
You must be signed in to change notification settings - Fork 649
feat: add gpu container support to microvm driver #1143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2901,6 +2901,30 @@ fn dockerfile_sources_supported_for_gateway(metadata: Option<&GatewayMetadata>) | |
| !metadata.is_some_and(|metadata| metadata.is_remote) | ||
| } | ||
|
|
||
| /// Load key=value pairs from a `versions.env` file in the given directory. | ||
| /// Returns an empty map if the file doesn't exist or can't be read. | ||
| fn load_versions_env(context: &Path) -> HashMap<String, String> { | ||
| let env_file = context.join("versions.env"); | ||
| let Ok(contents) = std::fs::read_to_string(&env_file) else { | ||
| return HashMap::new(); | ||
| }; | ||
| contents | ||
| .lines() | ||
| .filter_map(|line| { | ||
| let line = line.trim(); | ||
| if line.is_empty() || line.starts_with('#') { | ||
| return None; | ||
| } | ||
| let (key, value) = line.split_once('=')?; | ||
| let key = key.trim(); | ||
| if key.is_empty() { | ||
| return None; | ||
| } | ||
| Some((key.to_string(), value.trim().to_string())) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a question, how does this handle
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't strip quotes — they'd be included as literal characters in the value. Our |
||
| }) | ||
| .collect() | ||
| } | ||
|
|
||
| /// Build a Dockerfile and make the resulting image available to the gateway. | ||
| /// | ||
| /// For local Kubernetes gateways running in Docker, this imports the built image | ||
|
|
@@ -2935,6 +2959,14 @@ async fn build_from_dockerfile( | |
| eprintln!(" {} {}", "Gateway:".dimmed(), gateway_name); | ||
| eprintln!(); | ||
|
|
||
| let build_args = load_versions_env(context); | ||
| if !build_args.is_empty() { | ||
| for (k, v) in &build_args { | ||
| eprintln!(" Build arg (from versions.env): {k}={v}"); | ||
| } | ||
| eprintln!(); | ||
| } | ||
|
|
||
| let mut on_log = |msg: String| { | ||
| eprintln!(" {msg}"); | ||
| }; | ||
|
|
@@ -2943,7 +2975,7 @@ async fn build_from_dockerfile( | |
| dockerfile, | ||
| &tag, | ||
| context, | ||
| &HashMap::new(), | ||
| &build_args, | ||
| &mut on_log, | ||
| ) | ||
| .await?; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,11 @@ By default `mise run gateway:vm`: | |
|
|
||
| For GPU passthrough (VFIO), pass `-- --gpu` and run with root privileges: | ||
|
|
||
| > **Note:** GPU passthrough requires an **x86_64 host and guest**. The QEMU | ||
| > backend uses `qemu-system-x86_64`, and the NVIDIA driver installer / | ||
| > kernel module build scripts target x86_64 exclusively. ARM/aarch64 GPU | ||
| > passthrough is not yet supported. | ||
|
Comment on lines
+50
to
+53
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Is this detected somewhere?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not previously — you'd get a confusing QEMU failure. Added an early arch check in the GPU startup path that errors with "GPU passthrough requires x86_64" before attempting to launch QEMU. |
||
|
|
||
| ```shell | ||
| sudo -E env "PATH=$PATH" mise run gateway:vm -- --gpu | ||
| ``` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,8 @@ use std::path::{Path, PathBuf}; | |
| use std::{env, fs}; | ||
|
|
||
| fn main() { | ||
| emit_guest_kernel_version(); | ||
|
|
||
| println!("cargo:rerun-if-env-changed=OPENSHELL_VM_RUNTIME_COMPRESSED_DIR"); | ||
|
|
||
| if let Ok(dir) = env::var("OPENSHELL_VM_RUNTIME_COMPRESSED_DIR") { | ||
|
|
@@ -143,3 +145,57 @@ fn generate_stub_resources(out_dir: &Path, names: &[&str]) { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /// Parse `GUEST_KERNEL_VERSION` from `pins.env` and emit it as a compile-time | ||
| /// environment variable so `rootfs.rs` can use `env!("GUEST_KERNEL_VERSION")`. | ||
| fn emit_guest_kernel_version() { | ||
| let manifest_dir = | ||
| PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); | ||
| let pins_path = manifest_dir.join("../../crates/openshell-vm/pins.env"); | ||
|
|
||
| println!("cargo:rerun-if-changed={}", pins_path.display()); | ||
| println!("cargo:rerun-if-env-changed=GUEST_KERNEL_VERSION"); | ||
|
|
||
| let version = if let Ok(v) = env::var("GUEST_KERNEL_VERSION") { | ||
| v | ||
| } else if let Ok(contents) = fs::read_to_string(&pins_path) { | ||
| parse_guest_kernel_version(&contents).unwrap_or_else(|| { | ||
| panic!( | ||
| "GUEST_KERNEL_VERSION not found in {}", | ||
| pins_path.display() | ||
| ) | ||
| }) | ||
| } else { | ||
| panic!( | ||
| "Cannot read {} and GUEST_KERNEL_VERSION env var not set", | ||
| pins_path.display() | ||
| ); | ||
| }; | ||
|
|
||
| println!("cargo:rustc-env=GUEST_KERNEL_VERSION={version}"); | ||
| } | ||
|
|
||
| /// Extract the default value from a `GUEST_KERNEL_VERSION="${GUEST_KERNEL_VERSION:-<default>}"` | ||
| /// line in pins.env. | ||
| fn parse_guest_kernel_version(contents: &str) -> Option<String> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not critical for this PR, but we should reuse this logic if we add other envvars with similar logic.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — extracted into a generic |
||
| for line in contents.lines() { | ||
| let trimmed = line.trim(); | ||
| if trimmed.starts_with('#') || !trimmed.starts_with("GUEST_KERNEL_VERSION=") { | ||
| continue; | ||
| } | ||
| // Pattern: GUEST_KERNEL_VERSION="${GUEST_KERNEL_VERSION:-6.12.76}" | ||
| if let Some(start) = trimmed.find(":-") { | ||
| let after = &trimmed[start + 2..]; | ||
| if let Some(end) = after.find('}') { | ||
| let value = after[..end].trim_end_matches('"'); | ||
| return Some(value.to_string()); | ||
| } | ||
| } | ||
| // Fallback: simple assignment like GUEST_KERNEL_VERSION="6.12.76" | ||
| if let Some((_key, value)) = trimmed.split_once('=') { | ||
| let v = value.trim_matches('"').trim_matches('\''); | ||
| return Some(v.to_string()); | ||
| } | ||
| } | ||
| None | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This can be used to process any env file and not just
versions.env.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed — renamed to
load_env_fileand parameterized the filename.