Skip to content

cuda: stage shims for conda layouts and via SMOLVM_CUDA_STAGE_EXTRA_DIRS - #602

Open
NickyHeC wants to merge 6 commits into
smol-machines:mainfrom
NickyHeC:nickyhec/cuda-staging-conda-and-extra-dirs
Open

cuda: stage shims for conda layouts and via SMOLVM_CUDA_STAGE_EXTRA_DIRS#602
NickyHeC wants to merge 6 commits into
smol-machines:mainfrom
NickyHeC:nickyhec/cuda-staging-conda-and-extra-dirs

Conversation

@NickyHeC

@NickyHeC NickyHeC commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Refs #598. Complements #600. Safe to merge alone; full no-LD_PRELOAD conda torch still wants #638.

Problem

find_rpath_pinned_libs only matches pip-wheel paths (…/site-packages/nvidia/*/lib/<soname>). Conda (/opt/conda/lib, soname→versioned symlink) and vendored CUDA dirs miss staging → real cudart/cublas load.

Change (crates/smolvm-agent/src/cuda.rs, scripts/build-agent-rootfs.sh)

  • Treat /opt/conda/lib + /opt/conda/pkgs/*/lib as staged layouts.
  • Resolve soname symlinks lexically under the rootfs (avoid canonicalize escaping through host path).
  • SMOLVM_CUDA_STAGE_EXTRA_DIRS (colon-separated guest paths) escape hatch.
  • Standalone safety: packaging stamps runtime-capabilities with conda-overmount-v1 only after nm verifies the Runtime exports conda torch needs. Without that stamp, conda/extra-dir sonames are left untouched so the old LD_PRELOAD fall-through still works. Pip-wheel overmounts remain unconditional.

Unit: finds_conda_symlinked_libs, finds_extra_dir_libs, incomplete_runtime_preserves_conda_ld_preload_fallback, capable_runtime_stages_conda_without_ld_preload.

Standalone vs combined behavior

Config Conda discovery Conda overmount Torch import
This PR alone (incomplete shim, no stamp) yes skipped (real cudart kept; LD_PRELOAD fall-through preserved) falls back to pre-#602 behavior
This PR + capable shim (#638 exports stamped) yes yes no-LD_PRELOAD path

Proof — GPU (Lambda A10)

Image: pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime. Agent from this PR’s discovery commits. No LD_PRELOAD.

Tested commit set:

Staging (discovery alone, pre-gate): conda soname overmounted with ~901KB shim.
Torch import (discovery alone): FAIL — undefined symbol: cudaDeviceSetLimit (motivates the capability gate above).
Torch e2e (discovery + #638 stubs): cuda: True → matmul → tiny-Llama backward ok.

The capability gate itself is unit-tested; it was not re-run on A10.

@ghost

ghost commented Jul 12, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 3 individual chapters for you:

Title
1 Define layout detection and symlink resolution logic
2 Expand library scanning to include conda and extra dirs
3 Verify conda symlinks and extra dir scanning
Open in Stage

Chapters generated by Stage for commit f1b81c9 on Jul 15, 2026 6:07am UTC.

@NickyHeC
NickyHeC force-pushed the nickyhec/cuda-staging-conda-and-extra-dirs branch from e65c97a to 42a0560 Compare July 13, 2026 01:52
@BinSquare

Copy link
Copy Markdown
Contributor

Looks to be in the right direction - let's continue push for end to end validation

NickyHeC added 2 commits July 14, 2026 23:06
Auto-staging only recognized the pip-wheel layout (site-packages/nvidia/), so
conda images (pytorch/pytorch: libs at /opt/conda/lib/) and any vendored layout
silently loaded the real NVIDIA libraries and failed at runtime.

- Match the conda layout (/opt/conda/lib, /opt/conda/pkgs/*/lib) in addition to
  pip wheels.
- Match symlinked sonames (conda ships libcublas.so.12 -> libcublas.so.12.x.y),
  resolving the link lexically to overlay the real target while keeping the
  rootfs-relative path stage_shims needs.
- Add SMOLVM_CUDA_STAGE_EXTRA_DIRS as an escape hatch to point staging at
  arbitrary guest lib dirs.

Tests cover the conda symlink and extra-dir paths.

Refs smol-machines#598.
Collapse the strip_prefix chain onto one line to match `cargo fmt --all
-- --check`, fixing the failing fmt CI job on this branch.
@NickyHeC
NickyHeC force-pushed the nickyhec/cuda-staging-conda-and-extra-dirs branch from 42a0560 to f1b81c9 Compare July 15, 2026 06:07
@NickyHeC

Copy link
Copy Markdown
Contributor Author

GPU e2e — conda staging validated; torch import blocked by cudart shim surface

Rebased onto current main (tip f1b81c94) and exercised on Lambda A10 (Ubuntu 22.04, x86_64) with agent built from this branch. Packaged CUDA shims from #601 in the same rootfs. pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime, no LD_PRELOAD.

Staging discovery — PASS

Inside the guest after machine run --cuda:

/opt/smolvm-cuda/libcudart-shim.so   901240 (uid 1000)
/opt/conda/lib/libcudart.so.12 -> libcudart.so.12.4.127
/opt/conda/lib/libcudart.so.12.4.127 901240 (uid 1000)   ← bind-mounted shim, not the ~100MB real cudart

So /opt/conda/lib/ RPATH layout is found and overmounted as intended. SMOLVM_CUDA_STAGE_EXTRA_DIRS=/opt/conda/lib also exercised (smoke); staging already covered that path via the conda rules.

Torch e2e without LD_PRELOAD — FAIL (shim completeness, not discovery)

ImportError: .../libtorch_cuda.so: undefined symbol: cudaDeviceSetLimit, version libcudart.so.12

cudaDeviceSetLimit is not exported by smolvm-cudart-shim today. Once staging replaces the real libcudart.so.12* with the shim, torch's versioned dependency cannot be satisfied.

Notable side effect: classic LD_PRELOAD=/opt/smolvm-cuda/libcudart-shim.so no longer rescues this conda image either, because staging has already replaced the underlying soname — preload can no longer fall through to a full cudart for missing symbols. Pre-#602, staging missed conda paths so LD_PRELOAD worked with the real library remaining underneath.

Control — pip portal-cuda still OK

Same agent + shims, portal-cuda.tar (pip NVIDIA wheels):

cuda: True

Takeaway for merge gate

#602 piece Result
Conda path discovery + overmount PASS
SMOLVM_CUDA_STAGE_EXTRA_DIRS accepted / smoke OK
conda torch init / backward() sans LD_PRELOAD blocked on cudart shim API (cudaDeviceSetLimit at minimum)

Happy to land discovery as-is if the follow-up is a shim stub (or selective interposition) for missing Runtime API symbols — otherwise conda “drop LD_PRELOAD” stays blocked after this change lands, and the old LD_PRELOAD escape hatch regresses for staged conda layouts.

Host note (orthogonal): stock v1.6.0 libkrun.so needs GLIBC_2.39; this 22.04 box used v1.5.2 host libs to boot.

@NickyHeC

Copy link
Copy Markdown
Contributor Author

Follow-up: conda torch e2e now PASS (with cudart stub PR)

After the staging discovery above failed torch import on cudaDeviceSetLimit, we audited libtorch_cuda.so (conda pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime) against the shim. Missing UND symbols (8 total after adding SetLimit):

  • cudaDeviceSetLimit / cudaDeviceGetLimit
  • cudaGraphAddKernelNode / AddHostNode / AddEventRecordNode / AddEventWaitNode
  • cudaGraphRetainUserObject / cudaUserObjectCreate / cudaStreamUpdateCaptureDependencies

Shipped as draft #638 (nickyhec/cudart-stub-device-set-limit).

Retest on Lambda A10 — #602 agent + #638 cudart shim, no LD_PRELOAD

cuda: True
matmul_ok torch.Size([4, 4])
backward ok   # tiny-random-LlamaForCausalLM

So: #602 staging is sufficient for conda once the shim exports the Runtime API surface conda torch links against. Landing #602 alone without #638 (or equivalent) still regresses the old LD_PRELOAD fallthrough for incomplete shims.

@NickyHeC

Copy link
Copy Markdown
Contributor Author

Evidence update (description rewritten).

Staging alone: conda /opt/conda/lib/libcudart.so.12* overmounted (~901KB shim) — PASS.

Torch without LD_PRELOAD: needs Runtime exports from #638 (bind-mount removes fallthrough). Combined #602+#638: cuda: True, matmul, tiny Llama backward ok.

Land with #638; this PR alone regresses the old LD_PRELOAD escape hatch for staged sonames.

@NickyHeC

Copy link
Copy Markdown
Contributor Author

Following up on the end-to-end validation ask — that's now complete, so marking this ready for review.

e2e status (A10, Ubuntu 22.04): conda pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime, no LD_PRELOAD, agent built from this branch + shims from the released rootfs → cuda: True → matmul → tiny-Llama backward ok. The one caveat still holds: this PR needs #638 to work standalone — the bind-mount staging removes the old LD_PRELOAD fall-through, and conda libtorch_cuda.so needs the Runtime exports stubbed there. Combined #602 + #638 is the passing configuration.

Suggested landing: take the two together (I've marked both ready). Happy to reorder/rebase or fold the extra-dirs escape hatch however you'd prefer — just let me know.

Separately, while stress-testing the remoting path at scale I filed #667 (intermittent cold-start guest wedge on the first large fp32 load) — unrelated to this PR, noted only so it's on your radar.

@NickyHeC
NickyHeC marked this pull request as ready for review July 17, 2026 21:01
NickyHeC added a commit to NickyHeC/smolvm that referenced this pull request Jul 20, 2026
Conda libtorch_cuda.so version-requires cudaDeviceSetLimit@libcudart.so.12 at
import. After smol-machines#602 stages the shim over real cudart, that missing export breaks
dynamic linking. Mirror the driver shim's cuCtxSetLimit no-op / GetLimit default.
NickyHeC added 4 commits July 24, 2026 22:20
Bind-mounting the incomplete cudart shim over conda sonames removes the
old LD_PRELOAD fall-through. Stamp conda-overmount-v1 only after the
packaged shim exports the Runtime surface conda torch needs, and leave
conda/extra-dir libraries untouched otherwise.
BinSquare pushed a commit that referenced this pull request Jul 28, 2026
Conda libtorch_cuda.so version-requires cudaDeviceSetLimit@libcudart.so.12 at
import. After #602 stages the shim over real cudart, that missing export breaks
dynamic linking. Mirror the driver shim's cuCtxSetLimit no-op / GetLimit default.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants