gpu
gpu
¶
Vendor-aware GPU passthrough for podman containers.
The run.gpus selector names the GPU vendors a container should see:
"all" (or true) passes through every vendor the host detectably
supports, while "nvidia" / "amd" / "intel" — alone or as a
list — select vendors explicitly and fail loudly when the host cannot
serve one. Several vendors in one selector simply merge their podman
args, so a mixed-GPU host can hand all of its silicon to one container.
Per vendor, the mechanism follows the vendor's own container documentation — CDI when a host spec declares the vendor's device kind, the documented raw-device recipe otherwise:
- nvidia — CDI (
nvidia.com/gpu) when present; otherwise the pre-CDI Container Toolkit OCI hook (env-triggered), and as a last resort raw/dev/nvidia*device nodes for images that carry their own driver userland. - amd — CDI (
amd.com/gpu, generated byamd-ctk) when present; otherwise the ROCm-documented/dev/kfdplus the AMD render nodes. The raw recipe is also what works on older podman releases (< 4.1) that predate CDI. - intel — CDI (
intel.com/gpu) when present; otherwise the Intel render nodes — the oneAPI/NEO stack needs nothing else.
Raw DRM grants are vendor-scoped: only the granted vendor's render
nodes are mounted, never the whole /dev/dri (a mixed host's other
GPUs — or its BMC's display device — stay invisible). Each granted
node's /dev/dri/by-path symlink rides along read-only so
PCI-ordered enumeration keeps working inside the container.
A :N suffix ("amd:1", "nvidia:0,nvidia:1") grants single
devices instead of a vendor's whole fleet. On CDI hosts the index is
passed to the vendor's spec (amd.com/gpu=1) and means whatever the
vendor's tooling says it means. Without CDI the grant is best
effort: terok orders a vendor's devices by PCI bus address — stable
across launches (and across reboots for unchanged hardware) but not
guaranteed to match rocm-smi/nvidia-smi numbering — mounts only
the selected device nodes, and warns on stderr. Inside the container
the selected card enumerates as device 0.
AMD and Intel render nodes are group-gated (render), so those two
vendors additionally emit --group-add keep-groups — the
rootless-podman way to keep the invoking user's host groups. That flag
is honoured by crun only; check_gpu_error
translates the runc failure into a hint.
GPU_VENDORS = ('nvidia', 'amd', 'intel')
module-attribute
¶
Recognised run.gpus vendor tokens, in canonical (emission) order.
__all__ = ['GPU_VENDORS', 'GpuConfigError', 'GpuGrant', 'GpuSelector', 'GpuVendor', 'check_gpu_available', 'check_gpu_error', 'detect_gpu_vendors', 'gpu_device_addresses', 'gpu_run_args', 'normalize_gpus']
module-attribute
¶
GpuGrant = tuple[GpuVendor, int | None]
¶
One normalized selector token: a vendor and an optional device index.
("amd", None) grants every AMD device; ("amd", 1) grants the
host's second AMD device (PCI-bus-address order — see
terok_sandbox.runtime.gpu for the
ordering contract per passthrough tier).
GpuSelector = tuple[GpuGrant, ...] | Literal['all'] | None
¶
Normalized run.gpus value: explicit grants, auto-detect, or off.
The vocabulary lives here, next to the field it types; the host-probing
and podman-arg side of the story is terok_sandbox.runtime.gpu.
GpuVendor = Literal['nvidia', 'amd', 'intel']
¶
GpuConfigError(message, *, hint=_CDI_HINT)
¶
Bases: RuntimeError
GPU misconfiguration detected before or during container launch.
Store the remediation hint alongside the standard error message.
Source code in src/terok_sandbox/runtime/gpu.py
hint = hint
instance-attribute
¶
normalize_gpus(value)
¶
Normalize a raw run.gpus value into a GpuSelector.
Accepts the shapes a YAML config or CLI flag produces: booleans,
a single token ("all", "nvidia", "nvidia:0", …), a
comma-separated token string, or a list of tokens. A :N suffix
grants one device of that vendor instead of all of them; repeat the
vendor to grant several ("nvidia:0,nvidia:1"). A whole-vendor
token absorbs that vendor's indexed ones. Raises ValueError on
an unknown vendor or malformed index so misconfiguration surfaces
at parse time, not at launch.
Source code in src/terok_sandbox/config_schema.py
gpu_run_args(gpus=None)
¶
Return podman run args passing the selected GPU grants through.
"all" resolves against detect_gpu_vendors
and raises GpuConfigError when the host
supports none — a project that opted into GPUs should never silently
run without them. Explicit grants skip detection and raise
per-vendor when their prerequisites are missing.
Source code in src/terok_sandbox/runtime/gpu.py
detect_gpu_vendors()
¶
Return the GPU vendors this host can pass through right now.
A vendor counts as detected when a CDI spec declares its device
kind, or when its fallback-recipe markers are present — NVIDIA's
legacy OCI hook or /dev/nvidiactl, AMD's /dev/kfd plus an
AMD render node, an Intel render node.
Source code in src/terok_sandbox/runtime/gpu.py
check_gpu_available()
¶
Return True when a CDI spec declares the nvidia.com/gpu kind.
Wizards call this to decide whether to offer the NVIDIA base image;
the on-launch check_gpu_error
path is the authoritative one and stays in place. Any failure
(missing podman, missing CDI dirs, unreadable spec) collapses to
False so callers can treat this as a pure yes/no signal.
Source code in src/terok_sandbox/runtime/gpu.py
gpu_device_addresses()
¶
PCI bus addresses of each vendor's GPUs, in terok index order.
The authoritative answer to "which card does amd:1 grant" on the
raw (non-CDI) tiers: position N in a vendor's tuple is the
device that vendor:N selects. On CDI hosts the vendor's spec
owns the numbering instead and this map is informational only.
Source code in src/terok_sandbox/runtime/gpu.py
check_gpu_error(exc)
¶
Raise GpuConfigError if exc looks like a GPU launch issue.
Recognises CDI misconfiguration, the crun-only keep-groups
rejection, and legacy NVIDIA hook failures (typically the rootless
no-cgroups requirement); does nothing when the error matches
none of them. Defensively
handles both bytes and str stderr so callers that ran
subprocess with text=True are not punished with an
AttributeError on .decode.